FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int from, int to) const { PangoLayout* layout = getDefaultPangoLayout(run); setPangoAttributes(this, run, layout); gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length()); pango_layout_set_text(layout, utf8, -1); char* start = g_utf8_offset_to_pointer(utf8, from); char* end = g_utf8_offset_to_pointer(start, to - from); if (run.ltr()) { from = start - utf8; to = end - utf8; } else { from = end - utf8; to = start - utf8; } PangoLayoutLine* layoutLine = pango_layout_get_line_readonly(layout, 0); int x_pos; x_pos = 0; if (from < layoutLine->length) pango_layout_line_index_to_x(layoutLine, from, FALSE, &x_pos); float beforeWidth = PANGO_PIXELS_FLOOR(x_pos); x_pos = 0; if (run.ltr() || to < layoutLine->length) pango_layout_line_index_to_x(layoutLine, to, FALSE, &x_pos); float afterWidth = PANGO_PIXELS(x_pos); g_free(utf8); g_object_unref(layout); return FloatRect(point.x() + beforeWidth, point.y(), afterWidth - beforeWidth, h); }
/** Draw a TextLine object. * @param object The renderer object to use for transform and output * @param text_line The TextLine to render, including font and height. * @param pos The position to render it at. * @param color The color to render it with. */ static void draw_text_line (DiaRenderer *object, TextLine *text_line, Point *pos, Alignment alignment, Color *color) { DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object); GdkColor gdkcolor; int x,y; Point start_pos; PangoLayout* layout = NULL; const gchar *text = text_line_get_string(text_line); int height_pixels; real font_height = text_line_get_height(text_line); real scale = dia_transform_length(renderer->transform, 1.0); if (text == NULL || *text == '\0') return; /* Don't render empty strings. */ point_copy(&start_pos,pos); renderer_color_convert(renderer, color, &gdkcolor); height_pixels = dia_transform_length(renderer->transform, font_height); if (height_pixels < 2) { /* "Greeking" instead of making tiny font */ int width_pixels = dia_transform_length(renderer->transform, text_line_get_width(text_line)); gdk_gc_set_foreground(renderer->gc, &gdkcolor); gdk_gc_set_dashes(renderer->gc, 0, (gint8*)"\1\2", 2); dia_transform_coords(renderer->transform, start_pos.x, start_pos.y, &x, &y); gdk_draw_line(renderer->pixmap, renderer->gc, x, y, x + width_pixels, y); return; } else { start_pos.y -= text_line_get_ascent(text_line); start_pos.x -= text_line_get_alignment_adjustment (text_line, alignment); dia_transform_coords(renderer->transform, start_pos.x, start_pos.y, &x, &y); layout = dia_font_build_layout(text, text_line->font, dia_transform_length(renderer->transform, text_line->height)/20.0); #if defined(PANGO_VERSION_ENCODE) # if (PANGO_VERSION >= PANGO_VERSION_ENCODE(1,16,0)) /* I'd say the former Pango API was broken, i.e. leaky */ # define HAVE_pango_layout_get_line_readonly # endif #endif text_line_adjust_layout_line (text_line, #if defined(HAVE_pango_layout_get_line_readonly) pango_layout_get_line_readonly(layout, 0), #else pango_layout_get_line(layout, 0), #endif scale/20.0); if (renderer->highlight_color != NULL) { draw_highlighted_string(renderer, layout, x, y, &gdkcolor); } else { #if defined HAVE_FREETYPE { FT_Bitmap ftbitmap; int width, height; GdkPixbuf *rgba = NULL; width = dia_transform_length(renderer->transform, text_line_get_width(text_line)); height = dia_transform_length(renderer->transform, text_line_get_height(text_line)); if (width > 0) { int stride; guchar* pixels; int i,j; guint8 *graybitmap; initialize_ft_bitmap(&ftbitmap, width, height); pango_ft2_render_layout(&ftbitmap, layout, 0, 0); graybitmap = ftbitmap.buffer; rgba = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height); stride = gdk_pixbuf_get_rowstride(rgba); pixels = gdk_pixbuf_get_pixels(rgba); for (i = 0; i < height; i++) { for (j = 0; j < width; j++) { pixels[i*stride+j*4] = gdkcolor.red>>8; pixels[i*stride+j*4+1] = gdkcolor.green>>8; pixels[i*stride+j*4+2] = gdkcolor.blue>>8; pixels[i*stride+j*4+3] = graybitmap[i*ftbitmap.pitch+j]; } } g_free(graybitmap); gdk_draw_pixbuf(renderer->pixmap, renderer->gc, rgba, 0, 0, x, y, width, height, GDK_RGB_DITHER_NONE, 0, 0); g_object_unref(G_OBJECT(rgba)); } } #else gdk_gc_set_foreground(renderer->gc, &gdkcolor); gdk_draw_layout(renderer->pixmap, renderer->gc, x, y, layout); #endif } /* !higlight_color */ g_object_unref(G_OBJECT(layout)); } /* !greeking */
void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { #if USE(FREETYPE) if (!primaryFont()->platformData().m_pattern) { drawSimpleText(context, run, point, from, to); return; } #endif cairo_t* cr = context->platformContext()->cr(); PangoLayout* layout = pango_cairo_create_layout(cr); setPangoAttributes(this, run, layout); gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length()); pango_layout_set_text(layout, utf8, -1); // Our layouts are single line PangoLayoutLine* layoutLine = pango_layout_get_line_readonly(layout, 0); // Get the region where this text will be laid out. We will use it to clip // the Cairo context, for when we are only painting part of the text run and // to calculate the size of the shadow buffer. PangoRegionType partialRegion = 0; char* start = g_utf8_offset_to_pointer(utf8, from); char* end = g_utf8_offset_to_pointer(start, to - from); int ranges[] = {start - utf8, end - utf8}; #if PLATFORM(GTK) partialRegion = gdk_pango_layout_line_get_clip_region(layoutLine, 0, 0, ranges, 1); #else partialRegion = getClipRegionFromPangoLayoutLine(layoutLine, ranges); #endif drawGlyphsShadow(context, point, layoutLine, partialRegion); cairo_save(cr); cairo_translate(cr, point.x(), point.y()); float red, green, blue, alpha; context->fillColor().getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha); #if PLATFORM(GTK) gdk_cairo_region(cr, partialRegion); #else appendRegionToCairoContext(cr, partialRegion); #endif cairo_clip(cr); pango_cairo_show_layout_line(cr, layoutLine); if (context->textDrawingMode() & TextModeStroke) { Color strokeColor = context->strokeColor(); strokeColor.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha); pango_cairo_layout_line_path(cr, layoutLine); cairo_set_line_width(cr, context->strokeThickness()); cairo_stroke(cr); } // Pango sometimes leaves behind paths we don't want cairo_new_path(cr); destroyPangoRegion(partialRegion); g_free(utf8); g_object_unref(layout); cairo_restore(cr); }
void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { cairo_t* cr = context->platformContext(); cairo_save(cr); cairo_translate(cr, point.x(), point.y()); PangoLayout* layout = pango_cairo_create_layout(cr); setPangoAttributes(this, run, layout); gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length()); pango_layout_set_text(layout, utf8, -1); // Our layouts are single line PangoLayoutLine* layoutLine = pango_layout_get_line_readonly(layout, 0); GdkRegion* partialRegion = NULL; if (to - from != run.length()) { // Clip the region of the run to be rendered char* start = g_utf8_offset_to_pointer(utf8, from); char* end = g_utf8_offset_to_pointer(start, to - from); int ranges[] = {start - utf8, end - utf8}; partialRegion = gdk_pango_layout_line_get_clip_region(layoutLine, 0, 0, ranges, 1); gdk_region_shrink(partialRegion, 0, -pixelSize()); } Color fillColor = context->fillColor(); float red, green, blue, alpha; // Text shadow, inspired by FontMac IntSize shadowSize; int shadowBlur = 0; Color shadowColor; bool hasShadow = context->textDrawingMode() == cTextFill && context->getShadow(shadowSize, shadowBlur, shadowColor); // TODO: Blur support if (hasShadow) { // Disable graphics context shadows (not yet implemented) and paint them manually context->clearShadow(); Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); cairo_save(cr); shadowFillColor.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha); cairo_translate(cr, shadowSize.width(), shadowSize.height()); if (partialRegion) { gdk_cairo_region(cr, partialRegion); cairo_clip(cr); } pango_cairo_show_layout_line(cr, layoutLine); cairo_restore(cr); } fillColor.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha); if (partialRegion) { gdk_cairo_region(cr, partialRegion); cairo_clip(cr); } pango_cairo_show_layout_line(cr, layoutLine); if (context->textDrawingMode() & cTextStroke) { Color strokeColor = context->strokeColor(); strokeColor.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha); pango_cairo_layout_line_path(cr, layoutLine); cairo_set_line_width(cr, context->strokeThickness()); cairo_stroke(cr); } // Re-enable the platform shadow we disabled earlier if (hasShadow) context->setShadow(shadowSize, shadowBlur, shadowColor); // Pango sometimes leaves behind paths we don't want cairo_new_path(cr); if (partialRegion) gdk_region_destroy(partialRegion); g_free(utf8); g_object_unref(layout); cairo_restore(cr); }
void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area) { gint x,y,w; XftColor c; gint mw; PangoRectangle rect; PangoAttrList *attrlist; PangoEllipsizeMode ell; g_assert(!t->flow || t->maxwidth > 0); y = area->y; if (!t->flow) /* center the text vertically We do this centering based on the 'baseline' since different fonts have different top edges. It looks bad when the whole string is moved when 1 character from a non-default language is included in the string */ y += font_calculate_baseline(t->font, area->height); /* the +2 and -4 leave a small blank edge on the sides */ x = area->x + 2; w = area->width; if (t->flow) w = MAX(w, t->maxwidth); w -= 4; /* h = area->height; */ if (t->flow) ell = PANGO_ELLIPSIZE_NONE; else { switch (t->ellipsize) { case RR_ELLIPSIZE_NONE: ell = PANGO_ELLIPSIZE_NONE; break; case RR_ELLIPSIZE_START: ell = PANGO_ELLIPSIZE_START; break; case RR_ELLIPSIZE_MIDDLE: ell = PANGO_ELLIPSIZE_MIDDLE; break; case RR_ELLIPSIZE_END: ell = PANGO_ELLIPSIZE_END; break; default: g_assert_not_reached(); } } pango_layout_set_text(t->font->layout, t->string, -1); pango_layout_set_width(t->font->layout, w * PANGO_SCALE); pango_layout_set_ellipsize(t->font->layout, ell); pango_layout_set_single_paragraph_mode(t->font->layout, !t->flow); /* * * end of setting up the layout * * */ pango_layout_get_pixel_extents(t->font->layout, NULL, &rect); mw = rect.width; /* pango_layout_set_alignment doesn't work with pango_xft_render_layout_line */ switch (t->justify) { case RR_JUSTIFY_LEFT: break; case RR_JUSTIFY_RIGHT: x += (w - mw); break; case RR_JUSTIFY_CENTER: x += (w - mw) / 2; break; case RR_JUSTIFY_NUM_TYPES: g_assert_not_reached(); } if (t->shadow_offset_x || t->shadow_offset_y) { /* From nvidia's readme (chapter 23): When rendering to a 32-bit window, keep in mind that the X RENDER extension, used by most composite managers, expects "premultiplied alpha" colors. This means that if your color has components (r,g,b) and alpha value a, then you must render (a*r, a*g, a*b, a) into the target window. */ c.color.red = (t->shadow_color->r | t->shadow_color->r << 8) * t->shadow_alpha / 255; c.color.green = (t->shadow_color->g | t->shadow_color->g << 8) * t->shadow_alpha / 255; c.color.blue = (t->shadow_color->b | t->shadow_color->b << 8) * t->shadow_alpha / 255; c.color.alpha = 0xffff * t->shadow_alpha / 255; c.pixel = t->shadow_color->pixel; /* see below... */ if (!t->flow) { pango_xft_render_layout_line (d, &c, #if PANGO_VERSION_MAJOR > 1 || \ (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16) pango_layout_get_line_readonly(t->font->layout, 0), #else pango_layout_get_line(t->font->layout, 0), #endif (x + t->shadow_offset_x) * PANGO_SCALE, (y + t->shadow_offset_y) * PANGO_SCALE); } else { pango_xft_render_layout(d, &c, t->font->layout, (x + t->shadow_offset_x) * PANGO_SCALE, (y + t->shadow_offset_y) * PANGO_SCALE); } } c.color.red = t->color->r | t->color->r << 8; c.color.green = t->color->g | t->color->g << 8; c.color.blue = t->color->b | t->color->b << 8; c.color.alpha = 0xff | 0xff << 8; /* fully opaque text */ c.pixel = t->color->pixel; if (t->shortcut) { const gchar *s = t->string + t->shortcut_pos; t->font->shortcut_underline->start_index = t->shortcut_pos; t->font->shortcut_underline->end_index = t->shortcut_pos + (g_utf8_next_char(s) - s); /* the attributes are owned by the layout. re-add the attributes to the layout after changing the start and end index */ attrlist = pango_layout_get_attributes(t->font->layout); pango_attr_list_ref(attrlist); pango_layout_set_attributes(t->font->layout, attrlist); pango_attr_list_unref(attrlist); } /* layout_line() uses y to specify the baseline The line doesn't need to be freed, it's a part of the layout */ if (!t->flow) { pango_xft_render_layout_line (d, &c, #if PANGO_VERSION_MAJOR > 1 || \ (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16) pango_layout_get_line_readonly(t->font->layout, 0), #else pango_layout_get_line(t->font->layout, 0), #endif x * PANGO_SCALE, y * PANGO_SCALE); } else { pango_xft_render_layout(d, &c, t->font->layout, x * PANGO_SCALE, y * PANGO_SCALE); } if (t->shortcut) { t->font->shortcut_underline->start_index = 0; t->font->shortcut_underline->end_index = 0; /* the attributes are owned by the layout. re-add the attributes to the layout after changing the start and end index */ attrlist = pango_layout_get_attributes(t->font->layout); pango_attr_list_ref(attrlist); pango_layout_set_attributes(t->font->layout, attrlist); pango_attr_list_unref(attrlist); } }
static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag, uint32_t y) { uint32_t width = swaynag->width * swaynag->scale; int border = swaynag->type->details_border_thickness * swaynag->scale; int padding = swaynag->type->message_padding * swaynag->scale; int decor = padding + border; swaynag->details.x = decor; swaynag->details.y = y * swaynag->scale + decor; swaynag->details.width = width - decor * 2; PangoLayout *layout = get_pango_layout(cairo, swaynag->type->font, swaynag->details.message, swaynag->scale, false); pango_layout_set_width(layout, (swaynag->details.width - padding * 2) * PANGO_SCALE); pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); pango_layout_set_single_paragraph_mode(layout, false); pango_cairo_update_layout(cairo, layout); swaynag->details.total_lines = pango_layout_get_line_count(layout); PangoLayoutLine *line; line = pango_layout_get_line_readonly(layout, swaynag->details.offset); gint offset = line->start_index; const char *text = pango_layout_get_text(layout); pango_layout_set_text(layout, text + offset, strlen(text) - offset); int text_width, text_height; pango_cairo_update_layout(cairo, layout); pango_layout_get_pixel_size(layout, &text_width, &text_height); bool show_buttons = swaynag->details.offset > 0; int button_width = get_detailed_scroll_button_width(cairo, swaynag); if (show_buttons) { swaynag->details.width -= button_width; pango_layout_set_width(layout, (swaynag->details.width - padding * 2) * PANGO_SCALE); } uint32_t ideal_height; do { ideal_height = swaynag->details.y + text_height + decor + padding * 2; if (ideal_height > SWAYNAG_MAX_HEIGHT) { ideal_height = SWAYNAG_MAX_HEIGHT; if (!show_buttons) { show_buttons = true; swaynag->details.width -= button_width; pango_layout_set_width(layout, (swaynag->details.width - padding * 2) * PANGO_SCALE); } } swaynag->details.height = ideal_height - swaynag->details.y - decor; pango_layout_set_height(layout, (swaynag->details.height - padding * 2) * PANGO_SCALE); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); pango_cairo_update_layout(cairo, layout); pango_layout_get_pixel_size(layout, &text_width, &text_height); } while (text_height != (swaynag->details.height - padding * 2)); swaynag->details.visible_lines = pango_layout_get_line_count(layout); if (show_buttons) { swaynag->details.button_up.x = swaynag->details.x + swaynag->details.width; swaynag->details.button_up.y = swaynag->details.y; swaynag->details.button_up.width = button_width; swaynag->details.button_up.height = swaynag->details.height / 2; render_details_scroll_button(cairo, swaynag, &swaynag->details.button_up); swaynag->details.button_down.x = swaynag->details.x + swaynag->details.width; swaynag->details.button_down.y = swaynag->details.button_up.y + swaynag->details.button_up.height; swaynag->details.button_down.width = button_width; swaynag->details.button_down.height = swaynag->details.height / 2; render_details_scroll_button(cairo, swaynag, &swaynag->details.button_down); } cairo_set_source_u32(cairo, swaynag->type->border); cairo_rectangle(cairo, swaynag->details.x, swaynag->details.y, swaynag->details.width, swaynag->details.height); cairo_fill(cairo); cairo_move_to(cairo, swaynag->details.x + padding, swaynag->details.y + padding); cairo_set_source_u32(cairo, swaynag->type->text); pango_cairo_show_layout(cairo, layout); g_object_unref(layout); return ideal_height / swaynag->scale; }