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

1

7

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)

Alli answered 19/1, 2015 at 2:31 Comment(1)
Short guess: you are the first into it. Pango is not a full html renderer - just a text layout engine. But a well placed question nonetheless - maybe someone had trailed there before.Grata
H
10

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.

Hypnotist answered 23/5, 2015 at 17:47 Comment(6)
I ended up solving my problem by just outputting html+css and letting a browser handle rendering. Your answer shows more understanding of the problem than i originally had and is probably a reasonable stepping stone for someone else trying to solve this problem, cheers for the efforts :)Alli
Thanks so much. The Gtk/Pango world is really missing a programmers manual, the detail documentation isn't bad but how to put it all together... Oh yeah and getting around GtkTextBuffer's limitation is also what brought me here during my quest to write the perfect non HTML based markdown editorAshford
Mike, thanks for this demo! What is the license to the code on immortalsofar.com? I don't see a specific statement. Would you be willing to license it LGPL, BSD, or MIT in addition to the existing CC-BY-SA of this answer?Stannfield
@Ashford Any updates on the non-HTML Markdown editor? I am looking for lightweight md->pdf, and "lightweight" includes "doesn't require a full browser engine" :) . Thanks!Stannfield
No license, just help yourself. Full public domain.Hypnotist
@Hypnotist thanks very much! I took your code and ran with it (with credit) --- github.com/cxw42/pfft/blob/… . Part of a full Markdown->PDF converter using pangocairo. :) .Stannfield

© 2022 - 2024 — McMap. All rights reserved.