Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pango + Cairo; is there an existing approach for handling <img> style tags in text?

Tags:

ruby

cairo

pango

Pango syntax supports some text only markup. As far as i can see this does not extend to embedding images as well.

Looking around I cannot find much in the way of an existing implementation, but i havent done pango+cairo work before so i might be missing the obvious community for it.

As far as i can tell a reasonable approach would be to just analyse a string, pull out any tags, create cairo images, and then modify the pango layout around them accordingly.

It also seems like something someone might have done before.

Im specifically looking for an answer on these questions:

  1. Does pango+cairo already solve this and I have just misread the docs?
  2. Has something like this been done before, and where is a reference?
  3. Is this a reasonable approach, or should i try something else, and what?

(also note i am using ruby, so that may affect my options)

like image 516
Vusak Avatar asked Jan 19 '15 02:01

Vusak


1 Answers

I've been through the source of the markup parser and it does not allow for "shape" attributes (the way Pango almost incorporates graphics) but it is possible to do it "by hand".

Since there is absolutely no example code on the Web, here's Pango/Cairo/Images 101.

For a simple demo, I created an 800x400 window, added a GtkDrawingArea and connected up the "draw" signal. Before entering the main program loop, I initialized it with the following code:

PangoLayout     *Pango;
void init_drawingArea (GtkWidget *pWidget)
{
  cairo_surface_t *pImg = cairo_image_surface_create_from_png ("linux.png");
  PangoRectangle   r = {0, 0, PANGO_SCALE * cairo_image_surface_get_width (pImg),
                              PANGO_SCALE * cairo_image_surface_get_height(pImg)};
  PangoContext    *ctxt = gtk_widget_get_pango_context (pWidget);
  PangoAttrList   *attList = pango_attr_list_new();
  PangoAttribute  *attr;

  Pango = pango_layout_new (ctxt);

  pango_cairo_context_set_shape_renderer (ctxt, render, NULL, NULL);
  pango_layout_set_text (Pango, pszLorem, -1);
  pango_layout_set_width(Pango, PANGO_SCALE * 800);
  attr = pango_attr_shape_new_with_data(&r, &r, pImg, NULL, NULL);
  attr->start_index = 0; attr->end_index = 1;
  pango_attr_list_insert (attList, attr);

  attr = pango_attr_shape_new_with_data(&r, &r, pImg, NULL, NULL);
  attr->start_index = 152; attr->end_index = 153;
  pango_attr_list_insert (attList, attr);

  pango_layout_set_attributes (Pango, attList);
}

The context's shape renderer is set to render () and a PangoLayout is created and initialized. It then creates 2 shape attributes, sets the user data to a cairo surface which we populate from a png file and applies the attributes to characters 0 and 152 of the text.

The "draw" signal processing is straightforward.

gboolean onDraw (GtkWidget *pWidget, cairo_t *cr, gpointer user_data)
{
  pango_cairo_show_layout (cr, Pango);
  return 1;
}

and the render () PangoCairoShapeRenderFunc function is called as needed:

void render (cairo_t *cr, PangoAttrShape *pShape, gboolean do_path, gpointer data)
{
  cairo_surface_t *img = (cairo_surface_t *)pShape->data;
  double  dx, dy;

  cairo_get_current_point(cr, &dx, &dy);
  cairo_set_source_surface(cr, img, dx, dy);
  cairo_rectangle (cr, dx, dy, pShape->ink_rect.width/PANGO_SCALE,
                               pShape->ink_rect.height/PANGO_SCALE);
  cairo_fill(cr);
}

Taking the current point from cairo, it draws a rectangle and fills it with the image. This really sucks!

And that's pretty much all it does. Images were added as an afterthought and it shows. They are subject to the same rules as any other glyph so they are limited to the equivalent of CSS's display: inline.

I've put the code up at http://immortalsofar.com/PangoDemo/ if anyone wants to play with it. Me, I arrived here trying to get around GtkTextBuffer's limitations. Guess I'll just have to go deeper.

like image 174
Mike Avatar answered Sep 23 '22 02:09

Mike