Exemplo n.º 1
0
static gboolean
sup_parse_func      (MarkupData            *md,
		     OpenTag               *tag,
		     const gchar          **names,
		     const gchar          **values,
		     GMarkupParseContext   *context,
		     GError               **error)
{
  CHECK_NO_ATTRS("sup");

  /* Shrink font, and set a positive rise */
  if (tag)
    {
      tag->scale_level_delta -= 1;
      tag->scale_level -= 1;
    }

  add_attribute (tag, pango_attr_rise_new (SUPERSUB_RISE));

  return TRUE;
}
Exemplo n.º 2
0
/* This function is used by gtk_text_cell_accessible_get_offset_at_point()
 * and gtk_text_cell_accessible_get_character_extents(). There is no
 * cached PangoLayout so we must create a temporary one using this function.
 */
static PangoLayout *
create_pango_layout (GtkTextCellAccessible *text)
{
  GdkRGBA *foreground_rgba;
  PangoAttrList *attr_list, *attributes;
  PangoLayout *layout;
  PangoUnderline uline, underline;
  PangoFontMask mask;
  PangoFontDescription *font_desc;
  gboolean foreground_set, strikethrough_set, strikethrough;
  gboolean scale_set, underline_set, rise_set;
  gchar *renderer_text;
  gdouble scale;
  gint rise;
  GtkRendererCellAccessible *gail_renderer;
  GtkCellRendererText *gtk_renderer;

  gail_renderer = GTK_RENDERER_CELL_ACCESSIBLE (text);
  gtk_renderer = GTK_CELL_RENDERER_TEXT (gail_renderer->renderer);

  g_object_get (gtk_renderer,
                "text", &renderer_text,
                "attributes", &attributes,
                "foreground-set", &foreground_set,
                "foreground-rgba", &foreground_rgba,
                "strikethrough-set", &strikethrough_set,
                "strikethrough", &strikethrough,
                "font-desc", &font_desc,
                "scale-set", &scale_set,
                "scale", &scale,
                "underline-set", &underline_set,
                "underline", &underline,
                "rise-set", &rise_set,
                "rise", &rise,
                NULL);

  layout = gtk_widget_create_pango_layout (get_widget (text), renderer_text);

  if (attributes)
    attr_list = pango_attr_list_copy (attributes);
  else
    attr_list = pango_attr_list_new ();

  if (foreground_set)
    {
      add_attr (attr_list, pango_attr_foreground_new (foreground_rgba->red * 65535,
                                                      foreground_rgba->green * 65535,
                                                      foreground_rgba->blue * 65535));
    }

  if (strikethrough_set)
    add_attr (attr_list,
              pango_attr_strikethrough_new (strikethrough));

  mask = pango_font_description_get_set_fields (font_desc);

  if (mask & PANGO_FONT_MASK_FAMILY)
    add_attr (attr_list,
      pango_attr_family_new (pango_font_description_get_family (font_desc)));

  if (mask & PANGO_FONT_MASK_STYLE)
    add_attr (attr_list, pango_attr_style_new (pango_font_description_get_style (font_desc)));

  if (mask & PANGO_FONT_MASK_VARIANT)
    add_attr (attr_list, pango_attr_variant_new (pango_font_description_get_variant (font_desc)));

  if (mask & PANGO_FONT_MASK_WEIGHT)
    add_attr (attr_list, pango_attr_weight_new (pango_font_description_get_weight (font_desc)));

  if (mask & PANGO_FONT_MASK_STRETCH)
    add_attr (attr_list, pango_attr_stretch_new (pango_font_description_get_stretch (font_desc)));

  if (mask & PANGO_FONT_MASK_SIZE)
    add_attr (attr_list, pango_attr_size_new (pango_font_description_get_size (font_desc)));

  if (scale_set && scale != 1.0)
    add_attr (attr_list, pango_attr_scale_new (scale));

  if (underline_set)
    uline = underline;
  else
    uline = PANGO_UNDERLINE_NONE;

  if (uline != PANGO_UNDERLINE_NONE)
    add_attr (attr_list,
              pango_attr_underline_new (underline));

  if (rise_set)
    add_attr (attr_list, pango_attr_rise_new (rise));

  pango_layout_set_attributes (layout, attr_list);
  pango_layout_set_width (layout, -1);
  pango_attr_list_unref (attr_list);

  pango_font_description_free (font_desc);
  pango_attr_list_unref (attributes);
  g_free (renderer_text);
  gdk_rgba_free (foreground_rgba);

  return layout;
}
Exemplo n.º 3
0
/**
 * gimp_label_set_attributes:
 * @label: a #GtkLabel
 * @...:   a list of PangoAttrType and value pairs terminated by -1.
 *
 * Sets Pango attributes on a #GtkLabel in a more convenient way than
 * gtk_label_set_attributes().
 *
 * This function is useful if you want to change the font attributes
 * of a #GtkLabel. This is an alternative to using PangoMarkup which
 * is slow to parse and akward to handle in an i18n-friendly way.
 *
 * The attributes are set on the complete label, from start to end. If
 * you need to set attributes on part of the label, you will have to
 * use the PangoAttributes API directly.
 *
 * Since: 2.2
 **/
void
gimp_label_set_attributes (GtkLabel *label,
                           ...)
{
    PangoAttribute *attr  = NULL;
    PangoAttrList  *attrs;
    va_list         args;

    g_return_if_fail (GTK_IS_LABEL (label));

    attrs = pango_attr_list_new ();

    va_start (args, label);

    do
    {
        PangoAttrType attr_type = va_arg (args, PangoAttrType);

        if (attr_type == -1)
            attr_type = PANGO_ATTR_INVALID;

        switch (attr_type)
        {
        case PANGO_ATTR_LANGUAGE:
            attr = pango_attr_language_new (va_arg (args, PangoLanguage *));
            break;

        case PANGO_ATTR_FAMILY:
            attr = pango_attr_family_new (va_arg (args, const gchar *));
            break;

        case PANGO_ATTR_STYLE:
            attr = pango_attr_style_new (va_arg (args, PangoStyle));
            break;

        case PANGO_ATTR_WEIGHT:
            attr = pango_attr_weight_new (va_arg (args, PangoWeight));
            break;

        case PANGO_ATTR_VARIANT:
            attr = pango_attr_variant_new (va_arg (args, PangoVariant));
            break;

        case PANGO_ATTR_STRETCH:
            attr = pango_attr_stretch_new (va_arg (args, PangoStretch));
            break;

        case PANGO_ATTR_SIZE:
            attr = pango_attr_size_new (va_arg (args, gint));
            break;

        case PANGO_ATTR_FONT_DESC:
            attr = pango_attr_font_desc_new (va_arg (args,
                                             const PangoFontDescription *));
            break;

        case PANGO_ATTR_FOREGROUND:
        {
            const PangoColor *color = va_arg (args, const PangoColor *);

            attr = pango_attr_foreground_new (color->red,
                                              color->green,
                                              color->blue);
        }
        break;

        case PANGO_ATTR_BACKGROUND:
        {
            const PangoColor *color = va_arg (args, const PangoColor *);

            attr = pango_attr_background_new (color->red,
                                              color->green,
                                              color->blue);
        }
        break;

        case PANGO_ATTR_UNDERLINE:
            attr = pango_attr_underline_new (va_arg (args, PangoUnderline));
            break;

        case PANGO_ATTR_STRIKETHROUGH:
            attr = pango_attr_strikethrough_new (va_arg (args, gboolean));
            break;

        case PANGO_ATTR_RISE:
            attr = pango_attr_rise_new (va_arg (args, gint));
            break;

        case PANGO_ATTR_SCALE:
            attr = pango_attr_scale_new (va_arg (args, gdouble));
            break;

        default:
            g_warning ("%s: invalid PangoAttribute type %d",
                       G_STRFUNC, attr_type);
        case PANGO_ATTR_INVALID:
            attr = NULL;
            break;
        }

        if (attr)
        {
            attr->start_index = 0;
            attr->end_index   = -1;
            pango_attr_list_insert (attrs, attr);
        }
    }
    while (attr);

    va_end (args);

    gtk_label_set_attributes (label, attrs);
    pango_attr_list_unref (attrs);
}
Exemplo n.º 4
0
static void
go_pango_translate_here (PangoAttrIterator *state_iter,
			 PangoAttrIterator *attr_iter, PangoAttrList *attrs)
{
	double font_scale = 1.;
	double scale = 1.;
	int rise = 0;
	PangoAttribute *pa;
	PangoFontDescription *desc = pango_font_description_new ();
	GSList *the_attrs, *l;
	gint range_start, range_end;

	pango_attr_iterator_range (attr_iter, &range_start, &range_end);

	if (range_start == range_end)
		return;

	desc = pango_font_description_new ();
	pango_attr_iterator_get_font (state_iter, desc, NULL, NULL);
	font_scale = pango_font_description_get_size (desc)/
		(double)PANGO_SCALE/10.;
	pango_font_description_free (desc);

	pa = pango_attr_iterator_get
		(state_iter, PANGO_ATTR_SCALE);
	if (pa)
		scale = ((PangoAttrFloat *)pa)->value;
	pa = pango_attr_iterator_get
		(state_iter, PANGO_ATTR_RISE);
	if (pa)
		rise = ((PangoAttrInt *)pa)->value;

	/* We should probably figured out the default font size used */
	/* rather than assuming it is 10 pts * scale */
	if (font_scale == 0)
		font_scale = scale;

	the_attrs = pango_attr_iterator_get_attrs (attr_iter);
	for (l = the_attrs; l != NULL; l = l->next) {
		PangoAttribute *attribute = l->data;
		PangoAttrType type = attribute->klass->type;
		if (type == go_pango_attr_superscript_get_attr_type ()) {
			GOPangoAttrSuperscript *attr = (GOPangoAttrSuperscript *)attribute;
			if (attr->val) {
				scale *= GO_SUPERSCRIPT_SCALE;
				rise += GO_SUPERSCRIPT_RISE * font_scale;
				font_scale *= GO_SUPERSCRIPT_SCALE;
			}
		} else { /* go_pango_attr_subscript_type */
			GOPangoAttrSubscript *attr = (GOPangoAttrSubscript *)attribute;
			if (attr->val) {
				scale *= GO_SUBSCRIPT_SCALE;
				rise += GO_SUBSCRIPT_RISE * font_scale;
				font_scale *= GO_SUBSCRIPT_SCALE;
			}
		}
	}

	if (the_attrs != NULL) {
		PangoAttribute *attr = pango_attr_scale_new (scale);
		attr->start_index = range_start;
		attr->end_index = range_end;
		pango_attr_list_insert (attrs, attr);
		attr = pango_attr_rise_new (rise);
		attr->start_index = range_start;
		attr->end_index = range_end;
		pango_attr_list_insert (attrs, attr);
	}
	g_slist_free_full (the_attrs, (GDestroyNotify)pango_attribute_destroy);
}
Exemplo n.º 5
0
/* 
 * This function is used by gail_text_cell_get_offset_at_point()
 * and gail_text_cell_get_character_extents(). There is no 
 * cached PangoLayout for gailtextcell so we must create a temporary
 * one using this function.
 */ 
static PangoLayout*
create_pango_layout(GtkCellRendererText *gtk_renderer,
                    GtkWidget           *widget)
{
  PangoAttrList *attr_list;
  PangoLayout *layout;
  PangoUnderline uline;
  PangoFontMask mask;

  layout = gtk_widget_create_pango_layout (widget, gtk_renderer->text);

  if (gtk_renderer->extra_attrs)
    attr_list = pango_attr_list_copy (gtk_renderer->extra_attrs);
  else
    attr_list = pango_attr_list_new ();

  if (gtk_renderer->foreground_set)
    {
      PangoColor color;
      color = gtk_renderer->foreground;
      add_attr (attr_list, pango_attr_foreground_new (color.red,
                                                      color.green, color.blue));
    }

  if (gtk_renderer->strikethrough_set)
    add_attr (attr_list,
              pango_attr_strikethrough_new (gtk_renderer->strikethrough));

  mask = pango_font_description_get_set_fields (gtk_renderer->font);

  if (mask & PANGO_FONT_MASK_FAMILY)
    add_attr (attr_list,
      pango_attr_family_new (pango_font_description_get_family (gtk_renderer->font)));

  if (mask & PANGO_FONT_MASK_STYLE)
    add_attr (attr_list, pango_attr_style_new (pango_font_description_get_style (gtk_renderer->font)));

  if (mask & PANGO_FONT_MASK_VARIANT)
    add_attr (attr_list, pango_attr_variant_new (pango_font_description_get_variant (gtk_renderer->font)));

  if (mask & PANGO_FONT_MASK_WEIGHT)
    add_attr (attr_list, pango_attr_weight_new (pango_font_description_get_weight (gtk_renderer->font)));

  if (mask & PANGO_FONT_MASK_STRETCH)
    add_attr (attr_list, pango_attr_stretch_new (pango_font_description_get_stretch (gtk_renderer->font)));

  if (mask & PANGO_FONT_MASK_SIZE)
    add_attr (attr_list, pango_attr_size_new (pango_font_description_get_size (gtk_renderer->font)));

  if (gtk_renderer->scale_set &&
      gtk_renderer->font_scale != 1.0)
    add_attr (attr_list, pango_attr_scale_new (gtk_renderer->font_scale));

  if (gtk_renderer->underline_set)
    uline = gtk_renderer->underline_style;
  else
    uline = PANGO_UNDERLINE_NONE;

  if (uline != PANGO_UNDERLINE_NONE)
    add_attr (attr_list,
      pango_attr_underline_new (gtk_renderer->underline_style));

  if (gtk_renderer->rise_set)
    add_attr (attr_list, pango_attr_rise_new (gtk_renderer->rise));

  pango_layout_set_attributes (layout, attr_list);
  pango_layout_set_width (layout, -1);
  pango_attr_list_unref (attr_list);

  return layout;
}
Exemplo n.º 6
0
static void
decorate_text (GimpAboutDialog *dialog,
               gint             anim_type,
               gdouble          time)
{
  GtkStyle       *style = gtk_widget_get_style (dialog->anim_area);
  const gchar    *text;
  const gchar    *ptr;
  gint            letter_count = 0;
  gint            text_length  = 0;
  gint            text_bytelen = 0;
  gint            cluster_start, cluster_end;
  gunichar        unichr;
  PangoAttrList  *attrlist = NULL;
  PangoAttribute *attr;
  PangoRectangle  irect = {0, 0, 0, 0};
  PangoRectangle  lrect = {0, 0, 0, 0};
  GdkColor        mix;

  mix_colors (style->bg + GTK_STATE_NORMAL,
              style->fg + GTK_STATE_NORMAL, &mix, time);

  text = pango_layout_get_text (dialog->layout);
  g_return_if_fail (text != NULL);

  text_length = g_utf8_strlen (text, -1);
  text_bytelen = strlen (text);

  attrlist = pango_attr_list_new ();

  dialog->textrange[0] = 0;
  dialog->textrange[1] = text_bytelen;

  switch (anim_type)
    {
    case 0: /* Fade in */
      attr = pango_attr_foreground_new (mix.red, mix.green, mix.blue);
      attr->start_index = 0;
      attr->end_index = text_bytelen;
      pango_attr_list_insert (attrlist, attr);
      break;

    case 1: /* Fade in, spread */
      attr = pango_attr_foreground_new (mix.red, mix.green, mix.blue);
      attr->start_index = 0;
      attr->end_index = text_bytelen;
      pango_attr_list_change (attrlist, attr);

      ptr = text;

      cluster_start = 0;
      while ((unichr = g_utf8_get_char (ptr)))
        {
          ptr = g_utf8_next_char (ptr);
          cluster_end = (ptr - text);

          if (unichr == 0x200b)
            {
              lrect.width = (1.0 - time) * 15.0 * PANGO_SCALE + 0.5;
              attr = pango_attr_shape_new (&irect, &lrect);
              attr->start_index = cluster_start;
              attr->end_index = cluster_end;
              pango_attr_list_change (attrlist, attr);
            }
          cluster_start = cluster_end;
        }
      break;

    case 2: /* Fade in, sinewave */
      attr = pango_attr_foreground_new (mix.red, mix.green, mix.blue);
      attr->start_index = 0;
      attr->end_index = text_bytelen;
      pango_attr_list_change (attrlist, attr);

      ptr = text;

      cluster_start = 0;

      while ((unichr = g_utf8_get_char (ptr)))
        {
          if (unichr == 0x200b)
            {
              cluster_end = ptr - text;
              attr = pango_attr_rise_new ((1.0 -time) * 18000 *
                                          sin (4.0 * time +
                                               (float) letter_count * 0.7));
              attr->start_index = cluster_start;
              attr->end_index = cluster_end;
              pango_attr_list_change (attrlist, attr);

              letter_count++;
              cluster_start = cluster_end;
            }

          ptr = g_utf8_next_char (ptr);
        }
      break;

    case 3: /* letterwise Fade in */
      ptr = text;

      letter_count  = 0;
      cluster_start = 0;

      while ((unichr = g_utf8_get_char (ptr)))
        {
          gint    border = (text_length + 15) * time - 15;
          gdouble pos;

          if (letter_count < border)
            pos = 0;
          else if (letter_count > border + 15)
            pos = 1;
          else
            pos = ((gdouble) (letter_count - border)) / 15;

          mix_colors (style->fg + GTK_STATE_NORMAL,
                      style->bg + GTK_STATE_NORMAL,
                      &mix, pos);

          ptr = g_utf8_next_char (ptr);

          cluster_end = ptr - text;

          attr = pango_attr_foreground_new (mix.red, mix.green, mix.blue);
          attr->start_index = cluster_start;
          attr->end_index = cluster_end;
          pango_attr_list_change (attrlist, attr);

          if (pos < 1.0)
            dialog->textrange[1] = cluster_end;

          letter_count++;
          cluster_start = cluster_end;
        }

      break;

    default:
      g_printerr ("Unknown animation type %d\n", anim_type);
    }

  pango_layout_set_attributes (dialog->layout, attrlist);
  pango_attr_list_unref (attrlist);
}
static PangoAttrList*
hildon_font_selection_dialog_create_attrlist    (HildonFontSelectionDialog *fontsel, 
                                                 guint start_index, 
                                                 guint len)
{
    PangoAttrList *list;
    PangoAttribute *attr;
    gint size, position;
    gboolean family_set, size_set, color_set, bold, bold_set,
             italic, italic_set, underline, underline_set,
             strikethrough, strikethrough_set, position_set;
    GdkColor *color = NULL;
    gchar *family = NULL;
    gdouble font_scaling = 1.0;

    list = pango_attr_list_new ();

    g_object_get (G_OBJECT (fontsel),
            "family", &family, "family-set", &family_set,
            "size", &size, "size-set", &size_set,
            "color", &color, "color-set", &color_set,
            "bold", &bold, "bold-set", &bold_set,
            "italic", &italic, "italic-set", &italic_set,
            "underline", &underline, "underline-set", &underline_set,
            "strikethrough", &strikethrough, "strikethrough-set", 
            &strikethrough_set, "position", &position, 
            "position-set", &position_set, 
            "font-scaling", &font_scaling,
            NULL);

    /* family */
    if (family_set)
    {
        attr = pango_attr_family_new (family);
        add_preview_text_attr (list, attr, start_index, len);
    }
    g_free (family);

    /* size */
    if (size_set)
    {
        attr = pango_attr_size_new (size * PANGO_SCALE);
        add_preview_text_attr (list, attr, start_index, len);
    }

    /*color*/
    if (color_set)
    {
        attr = pango_attr_foreground_new (color->red, color->green, color->blue);
        add_preview_text_attr (list, attr, start_index, len);
    }

    if (color != NULL)
        gdk_color_free (color);

    /*weight*/
    if (bold_set)
    {
        if (bold)
            attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
        else
            attr = pango_attr_weight_new (PANGO_WEIGHT_NORMAL);

        add_preview_text_attr(list, attr, start_index, len);
    }

    /* style */
    if (italic_set)
    {
        if (italic)
            attr = pango_attr_style_new (PANGO_STYLE_ITALIC);
        else
            attr = pango_attr_style_new (PANGO_STYLE_NORMAL);

        add_preview_text_attr(list, attr, start_index, len);
    }

    /* underline */
    if (underline_set)
    {
        if (underline)
            attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
        else
            attr = pango_attr_underline_new (PANGO_UNDERLINE_NONE);

        add_preview_text_attr(list, attr, start_index, len);
    }

    /* strikethrough */
    if (strikethrough_set)
    {
        if (strikethrough)
            attr = pango_attr_strikethrough_new (TRUE);
        else
            attr = pango_attr_strikethrough_new (FALSE);

        add_preview_text_attr(list, attr, start_index, len);
    }

    /* position */
    if (position_set)
    {
        switch (position)
        {
            case 1: /*super*/
                attr = pango_attr_rise_new (SUPERSCRIPT_RISE);
                break;
            case -1: /*sub*/
                attr = pango_attr_rise_new (SUBSCRIPT_LOW);
                break;
            default: /*normal*/
                attr = pango_attr_rise_new (0);
                break;
        }

        add_preview_text_attr (list, attr, start_index, len);
    }

    /* font scaling for preview */
    if (font_scaling)
    {
        attr = pango_attr_scale_new(font_scaling);
        add_preview_text_attr(list, attr, 0, len + start_index);
    }

    return list;
}
Exemplo n.º 8
0
static gboolean
span_parse_func     (MarkupData            *md,
		     OpenTag               *tag,
		     const gchar          **names,
		     const gchar          **values,
		     GMarkupParseContext   *context,
		     GError               **error)
{
  int line_number, char_number;
  int i;

  const char *family = NULL;
  const char *size = NULL;
  const char *style = NULL;
  const char *weight = NULL;
  const char *variant = NULL;
  const char *stretch = NULL;
  const char *desc = NULL;
  const char *foreground = NULL;
  const char *background = NULL;
  const char *underline = NULL;
  const char *underline_color = NULL;
  const char *strikethrough = NULL;
  const char *strikethrough_color = NULL;
  const char *rise = NULL;
  const char *letter_spacing = NULL;
  const char *lang = NULL;
  const char *fallback = NULL;
  const char *gravity = NULL;
  const char *gravity_hint = NULL;

  g_markup_parse_context_get_position (context,
				       &line_number, &char_number);

#define CHECK_DUPLICATE(var) G_STMT_START{                              \
	  if ((var) != NULL) {                                          \
	    g_set_error (error, G_MARKUP_ERROR,                         \
			 G_MARKUP_ERROR_INVALID_CONTENT,                \
			 _("Attribute '%s' occurs twice on <span> tag " \
			   "on line %d char %d, may only occur once"),  \
			 names[i], line_number, char_number);           \
	    return FALSE;                                               \
	  }}G_STMT_END
#define CHECK_ATTRIBUTE2(var, name) \
	if (attr_strcmp (names[i], (name)) == 0) { \
	  CHECK_DUPLICATE (var); \
	  (var) = values[i]; \
	  found = TRUE; \
	  break; \
	}
#define CHECK_ATTRIBUTE(var) CHECK_ATTRIBUTE2 (var, G_STRINGIFY (var))

  i = 0;
  while (names[i])
    {
      gboolean found = FALSE;

      switch (names[i][0]) {
      case 'f':
	CHECK_ATTRIBUTE (fallback);
	CHECK_ATTRIBUTE2(desc, "font");
	CHECK_ATTRIBUTE2(desc, "font_desc");
	CHECK_ATTRIBUTE2(family, "face");

	CHECK_ATTRIBUTE2(family, "font_family");
	CHECK_ATTRIBUTE2(size, "font_size");
	CHECK_ATTRIBUTE2(stretch, "font_stretch");
	CHECK_ATTRIBUTE2(style, "font_style");
	CHECK_ATTRIBUTE2(variant, "font_variant");
	CHECK_ATTRIBUTE2(weight, "font_weight");

	CHECK_ATTRIBUTE (foreground);
	CHECK_ATTRIBUTE2 (foreground, "fgcolor");
	break;
      case 's':
	CHECK_ATTRIBUTE (size);
	CHECK_ATTRIBUTE (stretch);
	CHECK_ATTRIBUTE (strikethrough);
	CHECK_ATTRIBUTE (strikethrough_color);
	CHECK_ATTRIBUTE (style);
	break;
      case 'g':
	CHECK_ATTRIBUTE (gravity);
	CHECK_ATTRIBUTE (gravity_hint);
	break;
      case 'l':
	CHECK_ATTRIBUTE (lang);
	CHECK_ATTRIBUTE (letter_spacing);
	break;
      case 'u':
	CHECK_ATTRIBUTE (underline);
	CHECK_ATTRIBUTE (underline_color);
	break;
      default:
	CHECK_ATTRIBUTE (background);
	CHECK_ATTRIBUTE2 (background, "bgcolor");
	CHECK_ATTRIBUTE2(foreground, "color");
	CHECK_ATTRIBUTE (rise);
	CHECK_ATTRIBUTE (variant);
	CHECK_ATTRIBUTE (weight);
	break;
      }

      if (!found)
	{
	  g_set_error (error, G_MARKUP_ERROR,
		       G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
		       _("Attribute '%s' is not allowed on the <span> tag "
			 "on line %d char %d"),
		       names[i], line_number, char_number);
	  return FALSE;
	}

      ++i;
    }

  /* Parse desc first, then modify it with other font-related attributes. */
  if (G_UNLIKELY (desc))
    {
      PangoFontDescription *parsed;

      parsed = pango_font_description_from_string (desc);
      if (parsed)
	{
	  add_attribute (tag, pango_attr_font_desc_new (parsed));
	  if (tag)
	    open_tag_set_absolute_font_size (tag, pango_font_description_get_size (parsed));
	  pango_font_description_free (parsed);
	}
    }

  if (G_UNLIKELY (family))
    {
      add_attribute (tag, pango_attr_family_new (family));
    }

  if (G_UNLIKELY (size))
    {
      if (g_ascii_isdigit (*size))
	{
	  const char *end;
	  gint n;

/* cap size from the top at an arbitrary 2048 */
#define MAX_SIZE (2048 * PANGO_SCALE)

	  if ((end = size, !pango_scan_int (&end, &n)) || *end != '\0' || n < 0 || n > MAX_SIZE)
	    {
	      g_set_error (error,
			   G_MARKUP_ERROR,
			   G_MARKUP_ERROR_INVALID_CONTENT,
			   _("Value of 'size' attribute on <span> tag on line %d "
			     "could not be parsed; should be an integer less than %d, or a "
			     "string such as 'small', not '%s'"),
			   line_number, MAX_SIZE+1, size);
	      goto error;
	    }

	  add_attribute (tag, pango_attr_size_new (n));
	  if (tag)
	    open_tag_set_absolute_font_size (tag, n);
	}
      else if (strcmp (size, "smaller") == 0)
	{
	  if (tag)
	    {
	      tag->scale_level_delta -= 1;
	      tag->scale_level -= 1;
	    }
	}
      else if (strcmp (size, "larger") == 0)
	{
	  if (tag)
	    {
	      tag->scale_level_delta += 1;
	      tag->scale_level += 1;
	    }
	}
      else if (parse_absolute_size (tag, size))
	; /* nothing */
      else
	{
	  g_set_error (error,
		       G_MARKUP_ERROR,
		       G_MARKUP_ERROR_INVALID_CONTENT,
		       _("Value of 'size' attribute on <span> tag on line %d "
			 "could not be parsed; should be an integer, or a "
			 "string such as 'small', not '%s'"),
		       line_number, size);
	  goto error;
	}
    }

  if (G_UNLIKELY (style))
    {
      PangoStyle pango_style;

      if (pango_parse_style (style, &pango_style, FALSE))
	add_attribute (tag, pango_attr_style_new (pango_style));
      else
	{
	  g_set_error (error,
		       G_MARKUP_ERROR,
		       G_MARKUP_ERROR_INVALID_CONTENT,
		       _("'%s' is not a valid value for the 'style' attribute "
			 "on <span> tag, line %d; valid values are "
			 "'normal', 'oblique', 'italic'"),
		       style, line_number);
	  goto error;
	}
    }

  if (G_UNLIKELY (weight))
    {
      PangoWeight pango_weight;

      if (pango_parse_weight (weight, &pango_weight, FALSE))
	add_attribute (tag,
		       pango_attr_weight_new (pango_weight));
      else
	{
	  g_set_error (error,
		       G_MARKUP_ERROR,
		       G_MARKUP_ERROR_INVALID_CONTENT,
		       _("'%s' is not a valid value for the 'weight' "
			 "attribute on <span> tag, line %d; valid "
			 "values are for example 'light', 'ultrabold' or a number"),
		       weight, line_number);
	  goto error;
	}
    }

  if (G_UNLIKELY (variant))
    {
      PangoVariant pango_variant;

      if (pango_parse_variant (variant, &pango_variant, FALSE))
	add_attribute (tag, pango_attr_variant_new (pango_variant));
      else
	{
	  g_set_error (error,
		       G_MARKUP_ERROR,
		       G_MARKUP_ERROR_INVALID_CONTENT,
		       _("'%s' is not a valid value for the 'variant' "
			 "attribute on <span> tag, line %d; valid values are "
			 "'normal', 'smallcaps'"),
		       variant, line_number);
	  goto error;
	}
    }

  if (G_UNLIKELY (stretch))
    {
      PangoStretch pango_stretch;

      if (pango_parse_stretch (stretch, &pango_stretch, FALSE))
	add_attribute (tag, pango_attr_stretch_new (pango_stretch));
      else
	{
	  g_set_error (error,
		       G_MARKUP_ERROR,
		       G_MARKUP_ERROR_INVALID_CONTENT,
		       _("'%s' is not a valid value for the 'stretch' "
			 "attribute on <span> tag, line %d; valid "
			 "values are for example 'condensed', "
			 "'ultraexpanded', 'normal'"),
		       stretch, line_number);
	  goto error;
	}
    }

  if (G_UNLIKELY (foreground))
    {
      PangoColor color;

      if (!span_parse_color ("foreground", foreground, &color, line_number, error))
	goto error;

      add_attribute (tag, pango_attr_foreground_new (color.red, color.green, color.blue));
    }

  if (G_UNLIKELY (background))
    {
      PangoColor color;

      if (!span_parse_color ("background", background, &color, line_number, error))
	goto error;

      add_attribute (tag, pango_attr_background_new (color.red, color.green, color.blue));
    }

  if (G_UNLIKELY (underline))
    {
      PangoUnderline ul = PANGO_UNDERLINE_NONE;

      if (!span_parse_enum ("underline", underline, PANGO_TYPE_UNDERLINE, &ul, line_number, error))
	goto error;

      add_attribute (tag, pango_attr_underline_new (ul));
    }

  if (G_UNLIKELY (underline_color))
    {
      PangoColor color;

      if (!span_parse_color ("underline_color", underline_color, &color, line_number, error))
	goto error;

      add_attribute (tag, pango_attr_underline_color_new (color.red, color.green, color.blue));
    }

  if (G_UNLIKELY (gravity))
    {
      PangoGravity gr = PANGO_GRAVITY_SOUTH;

      if (!span_parse_enum ("gravity", gravity, PANGO_TYPE_GRAVITY, &gr, line_number, error))
	goto error;

      add_attribute (tag, pango_attr_gravity_new (gr));
    }

  if (G_UNLIKELY (gravity_hint))
    {
      PangoGravityHint hint = PANGO_GRAVITY_HINT_NATURAL;

      if (!span_parse_enum ("gravity_hint", gravity_hint, PANGO_TYPE_GRAVITY_HINT, &hint, line_number, error))
	goto error;

      add_attribute (tag, pango_attr_gravity_hint_new (hint));
    }

  if (G_UNLIKELY (strikethrough))
    {
      gboolean b = FALSE;

      if (!span_parse_boolean ("strikethrough", strikethrough, &b, line_number, error))
	goto error;

      add_attribute (tag, pango_attr_strikethrough_new (b));
    }

  if (G_UNLIKELY (strikethrough_color))
    {
      PangoColor color;

      if (!span_parse_color ("strikethrough_color", strikethrough_color, &color, line_number, error))
	goto error;

      add_attribute (tag, pango_attr_strikethrough_color_new (color.red, color.green, color.blue));
    }

  if (G_UNLIKELY (fallback))
    {
      gboolean b = FALSE;

      if (!span_parse_boolean ("fallback", fallback, &b, line_number, error))
	goto error;

      add_attribute (tag, pango_attr_fallback_new (b));
    }

  if (G_UNLIKELY (rise))
    {
      gint n = 0;

      if (!span_parse_int ("rise", rise, &n, line_number, error))
	goto error;

      add_attribute (tag, pango_attr_rise_new (n));
    }

  if (G_UNLIKELY (letter_spacing))
    {
      gint n = 0;

      if (!span_parse_int ("letter_spacing", letter_spacing, &n, line_number, error))
	goto error;

      add_attribute (tag, pango_attr_letter_spacing_new (n));
    }

  if (G_UNLIKELY (lang))
    {
      add_attribute (tag,
		     pango_attr_language_new (pango_language_from_string (lang)));
    }

  return TRUE;

 error:

  return FALSE;
}