From 366cbe701c398782c4543313625187e7fa297135 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 20 Jun 2022 06:29:30 -0700 Subject: Bring back shape attributes It turns out that using user fonts for embedding is a bit too involved, so lets keep the shape attribute machinery around. --- pango/pango-attr-list.c | 1 + pango/pango-attr.c | 93 ++++++++++++++++++++-------------- pango/pango-attributes-private.h | 9 ++++ pango/pango-attributes.c | 44 ++++++++++++++++ pango/pango-attributes.h | 8 ++- pango/pango-item-private.h | 1 + pango/pango-item.c | 5 ++ pango/pango-line-breaker.c | 37 ++++++++++++-- pango/pango-renderer.c | 105 +++++++++++++++++++++++++++++++++++---- pango/pango-renderer.h | 12 +++-- pango/pango-run.c | 57 ++++++++++++++++++++- pango/serializer.c | 1 + 12 files changed, 315 insertions(+), 58 deletions(-) diff --git a/pango/pango-attr-list.c b/pango/pango-attr-list.c index 0cf381b1..3f5c00f8 100644 --- a/pango/pango-attr-list.c +++ b/pango/pango-attr-list.c @@ -1253,6 +1253,7 @@ pango_attr_list_from_string (const char *text) INT_ATTR(line_spacing, int); break; + case PANGO_ATTR_SHAPE: default: g_assert_not_reached (); } diff --git a/pango/pango-attr.c b/pango/pango-attr.c index 9640e009..211d8232 100644 --- a/pango/pango-attr.c +++ b/pango/pango-attr.c @@ -23,6 +23,7 @@ #include #include "pango-attr-private.h" +#include "pango-attributes-private.h" #include "pango-impl-utils.h" @@ -98,6 +99,7 @@ is_valid_attr_type (guint type) case PANGO_ATTR_SENTENCE: case PANGO_ATTR_BASELINE_SHIFT: case PANGO_ATTR_FONT_SCALE: + case PANGO_ATTR_SHAPE: return TRUE; default: if (!attr_type) @@ -265,28 +267,39 @@ pango_attribute_copy (const PangoAttribute *attr) break; case PANGO_ATTR_VALUE_POINTER: - { - PangoAttrDataCopyFunc copy = NULL; + if (attr->type == PANGO_ATTR_SHAPE) + { + ShapeData *shape_data; - G_LOCK (attr_type); + shape_data = g_memdup2 (attr->pointer_value, sizeof (ShapeData)); + if (shape_data->copy) + shape_data->data = shape_data->copy (shape_data->data); - g_assert (attr_type != NULL); + result->pointer_value = shape_data; + } + else + { + PangoAttrDataCopyFunc copy = NULL; - for (int i = 0; i < attr_type->len; i++) - { - PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i); - if (class->type == attr->type) - { - copy = class->copy; - break; - } - } + G_LOCK (attr_type); - G_UNLOCK (attr_type); + g_assert (attr_type != NULL); - if (copy) - result->pointer_value = copy (attr->pointer_value); - } + for (int i = 0; i < attr_type->len; i++) + { + PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i); + if (class->type == attr->type) + { + copy = class->copy; + break; + } + } + + G_UNLOCK (attr_type); + + if (copy) + result->pointer_value = copy (attr->pointer_value); + } break; case PANGO_ATTR_VALUE_INT: @@ -322,28 +335,38 @@ pango_attribute_destroy (PangoAttribute *attr) break; case PANGO_ATTR_VALUE_POINTER: - { - GDestroyNotify destroy = NULL; + if (attr->type == PANGO_ATTR_SHAPE) + { + ShapeData *shape_data = attr->pointer_value; - G_LOCK (attr_type); + if (shape_data->destroy) + shape_data->destroy (shape_data->data); - g_assert (attr_type != NULL); + g_free (shape_data); + } + else + { + GDestroyNotify destroy = NULL; - for (int i = 0; i < attr_type->len; i++) - { - PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i); - if (class->type == attr->type) - { - destroy = class->destroy; - break; - } - } + G_LOCK (attr_type); - G_UNLOCK (attr_type); + g_assert (attr_type != NULL); - if (destroy) - destroy (attr->pointer_value); - } + for (int i = 0; i < attr_type->len; i++) + { + PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i); + if (class->type == attr->type) + { + destroy = class->destroy; + break; + } + } + + G_UNLOCK (attr_type); + + if (destroy) + destroy (attr->pointer_value); + } break; case PANGO_ATTR_VALUE_INT: @@ -427,8 +450,6 @@ pango_attribute_equal (const PangoAttribute *attr1, G_UNLOCK (attr_type); - g_assert (equal != NULL); - if (equal) return equal (attr1->pointer_value, attr2->pointer_value); diff --git a/pango/pango-attributes-private.h b/pango/pango-attributes-private.h index 3ff30529..181fe98d 100644 --- a/pango/pango-attributes-private.h +++ b/pango/pango-attributes-private.h @@ -25,3 +25,12 @@ gboolean pango_attribute_affects_itemization (PangoAttribute *attr, gpointer data); gboolean pango_attribute_affects_break_or_shape (PangoAttribute *attr, gpointer data); + +typedef struct { + PangoRectangle ink_rect; + PangoRectangle logical_rect; + gpointer data; + PangoAttrDataCopyFunc copy; + GDestroyNotify destroy; +} ShapeData; + diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c index 253feca9..c1dc26d2 100644 --- a/pango/pango-attributes.c +++ b/pango/pango-attributes.c @@ -799,6 +799,50 @@ pango_attr_text_transform_new (PangoTextTransform transform) return pango_attr_int_new (PANGO_ATTR_TEXT_TRANSFORM, transform); } +/** + * pango_attr_shape_new: + * @ink_rect: ink rectangle to use for each character + * @logical_rect: logical rectangle to use for each character + * @data: user data + * @copy: (nullable): function to copy @data when the attribute + * is copied + * @destroy: (nullable): function to free @data when the attribute + * is freed + * + * Creates a new shape attribute. + * + * Shape attributes override the extents for a glyph and can + * trigger custom rendering in a `PangoRenderer`. This might + * be used, for instance, for embedding a picture or a widget + * inside a `PangoLayout`. + * + * Return value: (transfer full): the newly allocated + * `PangoAttribute`, which should be freed with + * [method@Pango.Attribute.destroy] + */ +PangoAttribute * +pango_attr_shape_new (PangoRectangle *ink_rect, + PangoRectangle *logical_rect, + gpointer data, + PangoAttrDataCopyFunc copy, + GDestroyNotify destroy) +{ + PangoAttribute *attr; + ShapeData *shape_data; + + shape_data = g_new0 (ShapeData, 1); + shape_data->ink_rect = *ink_rect; + shape_data->logical_rect = *logical_rect; + shape_data->data = data; + shape_data->copy = copy; + shape_data->destroy = destroy; + + attr = pango_attr_init (PANGO_ATTR_SHAPE); + attr->pointer_value = shape_data; + + return attr; +} + /* }}} */ /* {{{ Private API */ diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h index a92350a8..ffb3d07f 100644 --- a/pango/pango-attributes.h +++ b/pango/pango-attributes.h @@ -118,11 +118,11 @@ typedef enum PANGO_ATTR_BASELINE_SHIFT = PANGO_ATTR_TYPE (INT, ITEMIZATION, ACCUMULATES), PANGO_ATTR_FONT_SCALE = PANGO_ATTR_TYPE (INT, ITEMIZATION, ACCUMULATES), PANGO_ATTR_LINE_SPACING = PANGO_ATTR_TYPE (INT, ITEMIZATION, OVERRIDES), + PANGO_ATTR_SHAPE = PANGO_ATTR_TYPE (POINTER, ITEMIZATION, OVERRIDES), } PangoAttrType; #undef PANGO_ATTR_TYPE - PANGO_AVAILABLE_IN_ALL PangoAttribute * pango_attr_language_new (PangoLanguage *language); PANGO_AVAILABLE_IN_ALL @@ -301,5 +301,11 @@ typedef enum { PANGO_AVAILABLE_IN_ALL PangoAttribute * pango_attr_text_transform_new (PangoTextTransform transform); +PANGO_AVAILABLE_IN_ALL +PangoAttribute * pango_attr_shape_new (PangoRectangle *ink_rect, + PangoRectangle *logical_rect, + gpointer data, + PangoAttrDataCopyFunc copy, + GDestroyNotify destroy); G_END_DECLS diff --git a/pango/pango-item-private.h b/pango/pango-item-private.h index 541eb017..3c8b18d7 100644 --- a/pango/pango-item-private.h +++ b/pango/pango-item-private.h @@ -116,6 +116,7 @@ struct _ItemProperties int line_spacing; int absolute_line_height; double line_height; + PangoAttribute *shape; }; void pango_item_get_properties (PangoItem *item, diff --git a/pango/pango-item.c b/pango/pango-item.c index 079f5796..952cab05 100644 --- a/pango/pango-item.c +++ b/pango/pango-item.c @@ -401,6 +401,7 @@ pango_item_get_properties (PangoItem *item, properties->line_height = 0.0; properties->absolute_line_height = 0; properties->line_spacing = 0; + properties->shape = NULL; while (tmp_list) { @@ -448,6 +449,10 @@ pango_item_get_properties (PangoItem *item, properties->no_paragraph_break = TRUE; break; + case PANGO_ATTR_SHAPE: + properties->shape = attr; + break; + default: break; } diff --git a/pango/pango-line-breaker.c b/pango/pango-line-breaker.c index 55eb9f4f..6fe52dc3 100644 --- a/pango/pango-line-breaker.c +++ b/pango/pango-line-breaker.c @@ -849,6 +849,28 @@ distribute_letter_spacing (int letter_spacing, *space_right = letter_spacing - *space_left; } +static void +pango_shape_shape (const char *text, + unsigned int n_chars, + ShapeData *shape, + PangoGlyphString *glyphs) +{ + unsigned int i; + const char *p; + + pango_glyph_string_set_size (glyphs, n_chars); + + for (i = 0, p = text; i < n_chars; i++, p = g_utf8_next_char (p)) + { + glyphs->glyphs[i].glyph = PANGO_GLYPH_EMPTY; + glyphs->glyphs[i].geometry.x_offset = 0; + glyphs->glyphs[i].geometry.y_offset = 0; + glyphs->glyphs[i].geometry.width = shape->logical_rect.width; + glyphs->glyphs[i].attr.is_cluster_start = 1; + + glyphs->log_clusters[i] = p - text; + } +} static PangoGlyphString * shape_run (PangoLineBreaker *self, @@ -866,11 +888,16 @@ shape_run (PangoLineBreaker *self, if (pango_context_get_round_glyph_positions (self->context)) shape_flags |= PANGO_SHAPE_ROUND_POSITIONS; - pango_shape_item (item, - self->data->text, self->data->length, - self->data->log_attrs + self->start_offset, - glyphs, - shape_flags); + if (self->properties.shape) + pango_shape_shape (self->data->text + item->offset, item->num_chars, + (ShapeData *)self->properties.shape->pointer_value, + glyphs); + else + pango_shape_item (item, + self->data->text, self->data->length, + self->data->log_attrs + self->start_offset, + glyphs, + shape_flags); if (self->properties.letter_spacing) { diff --git a/pango/pango-renderer.c b/pango/pango-renderer.c index 5c94be74..adaeb70f 100644 --- a/pango/pango-renderer.c +++ b/pango/pango-renderer.c @@ -27,6 +27,7 @@ #include "pango-layout.h" #include "pango-run-private.h" #include "pango-line-private.h" +#include "pango-attributes-private.h" #define N_RENDER_PARTS 5 @@ -505,6 +506,34 @@ static void pango_renderer_draw_runs (PangoRenderer *renderer, int x, int y); +static void +draw_shaped_glyphs (PangoRenderer *renderer, + PangoGlyphString *glyphs, + PangoAttribute *attr, + int x, + int y) +{ + PangoRendererClass *class = PANGO_RENDERER_GET_CLASS (renderer); + int i; + + if (!class->draw_shape) + return; + + for (i = 0; i < glyphs->num_glyphs; i++) + { + PangoGlyphInfo *gi = &glyphs->glyphs[i]; + ShapeData *data = (ShapeData *)attr->pointer_value; + + class->draw_shape (renderer, + &data->ink_rect, + &data->logical_rect, + data->data, + x, y); + + x += gi->geometry.width; + } +} + /** * pango_renderer_draw_line: * @renderer: a `PangoRenderer` @@ -598,6 +627,41 @@ pango_renderer_draw_lines (PangoRenderer *renderer, renderer->priv->lines = NULL; } +static void +pango_shape_get_extents (int n_chars, + PangoRectangle *shape_ink, + PangoRectangle *shape_logical, + PangoRectangle *ink_rect, + PangoRectangle *logical_rect) +{ + if (n_chars > 0) + { + ink_rect->x = MIN (shape_ink->x, shape_ink->x + shape_logical->width * (n_chars - 1)); + ink_rect->width = MAX (shape_ink->width, shape_ink->width + shape_logical->width * (n_chars - 1)); + ink_rect->y = shape_ink->y; + ink_rect->height = shape_ink->height; + + logical_rect->x = MIN (shape_logical->x, shape_logical->x + shape_logical->width * (n_chars - 1)); + logical_rect->width = MAX (shape_logical->width, shape_logical->width + shape_logical->width * (n_chars - 1)); + logical_rect->y = shape_logical->y; + logical_rect->height = shape_logical->height; + } + else + { + ink_rect->x = 0; + ink_rect->y = 0; + ink_rect->width = 0; + ink_rect->height = 0; + + logical_rect->x = 0; + logical_rect->y = 0; + logical_rect->width = 0; + logical_rect->height = 0; + } +} + + + static void pango_renderer_draw_runs (PangoRenderer *renderer, PangoLine *line, @@ -621,6 +685,7 @@ pango_renderer_draw_runs (PangoRenderer *renderer, PangoGlyphString *glyphs = glyph_item->glyphs; PangoRectangle ink_rect, *ink = NULL; PangoRectangle logical_rect, *logical = NULL; + ItemProperties properties; int y_off; if (glyph_item->item->analysis.flags & PANGO_ANALYSIS_FLAG_CENTERED_BASELINE) @@ -628,19 +693,38 @@ pango_renderer_draw_runs (PangoRenderer *renderer, pango_renderer_prepare_run (renderer, run); - if (renderer->underline != PANGO_LINE_STYLE_NONE || - renderer->overline != PANGO_LINE_STYLE_NONE || - renderer->strikethrough != PANGO_LINE_STYLE_NONE) + pango_item_get_properties (item, &properties); + + if (properties.shape) { + ShapeData *data = (ShapeData *)properties.shape->pointer_value; + ink = &ink_rect; logical = &logical_rect; + pango_shape_get_extents (glyphs->num_glyphs, + &data->ink_rect, + &data->logical_rect, + ink, + logical); + + glyph_string_width = logical->width; } - if (G_UNLIKELY (ink || logical)) - pango_glyph_string_extents (glyphs, item->analysis.font, ink, logical); - if (logical) - glyph_string_width = logical_rect.width; else - glyph_string_width = pango_glyph_string_get_width (glyphs); + { + if (renderer->underline != PANGO_LINE_STYLE_NONE || + renderer->overline != PANGO_LINE_STYLE_NONE || + renderer->strikethrough != PANGO_LINE_STYLE_NONE) + { + ink = &ink_rect; + logical = &logical_rect; + } + if (G_UNLIKELY (ink || logical)) + pango_glyph_string_extents (glyphs, item->analysis.font, ink, logical); + if (logical) + glyph_string_width = logical_rect.width; + else + glyph_string_width = pango_glyph_string_get_width (glyphs); + } renderer->priv->line_state->logical_rect_end = x + x_off + glyph_string_width; @@ -674,7 +758,10 @@ pango_renderer_draw_runs (PangoRenderer *renderer, overall_rect.height); } - pango_renderer_draw_glyph_item (renderer, text, glyph_item, x + x_off, y - y_off); + if (properties.shape) + draw_shaped_glyphs (renderer, glyphs, properties.shape, x + x_off, y - y_off); + else + pango_renderer_draw_glyph_item (renderer, text, glyph_item, x + x_off, y - y_off); if (renderer->underline != PANGO_LINE_STYLE_NONE || renderer->overline != PANGO_LINE_STYLE_NONE || diff --git a/pango/pango-renderer.h b/pango/pango-renderer.h index 954ffea8..ab70f183 100644 --- a/pango/pango-renderer.h +++ b/pango/pango-renderer.h @@ -127,7 +127,6 @@ struct _PangoRendererClass /*< private >*/ GObjectClass parent_class; - /* vtable - not signals */ /*< public >*/ void (*draw_glyphs) (PangoRenderer *renderer, @@ -146,7 +145,12 @@ struct _PangoRendererClass int y, int width, int height); - + void (*draw_shape) (PangoRenderer *renderer, + PangoRectangle *ink_rect, + PangoRectangle *logical_rect, + gpointer data, + int x, + int y); void (*draw_trapezoid) (PangoRenderer *renderer, PangoRenderPart part, double y1_, @@ -179,9 +183,7 @@ struct _PangoRendererClass /*< private >*/ /* Padding for future expansion */ - void (*_pango_reserved2) (void); - void (*_pango_reserved3) (void); - void (*_pango_reserved4) (void); + gpointer _pango_reserved[8]; }; PANGO_AVAILABLE_IN_ALL diff --git a/pango/pango-run.c b/pango/pango-run.c index 23f495aa..d3138cb6 100644 --- a/pango/pango-run.c +++ b/pango/pango-run.c @@ -4,6 +4,7 @@ #include "pango-item-private.h" #include "pango-impl-utils.h" #include "pango-font-metrics-private.h" +#include "pango-attributes-private.h" #include @@ -48,6 +49,54 @@ pango_run_get_glyphs (PangoRun *run) return run->glyph_item.glyphs; } +static void +pango_shape_get_extents (int n_chars, + PangoAttribute *attr, + PangoRectangle *ink_rect, + PangoRectangle *logical_rect) +{ + if (n_chars > 0) + { + ShapeData *data = (ShapeData *)attr->pointer_value; + PangoRectangle *shape_ink = &data->ink_rect; + PangoRectangle *shape_logical = &data->logical_rect; + + if (ink_rect) + { + ink_rect->x = MIN (shape_ink->x, shape_ink->x + shape_logical->width * (n_chars - 1)); + ink_rect->width = MAX (shape_ink->width, shape_ink->width + shape_logical->width * (n_chars - 1)); + ink_rect->y = shape_ink->y; + ink_rect->height = shape_ink->height; + } + + if (logical_rect) + { + logical_rect->x = MIN (shape_logical->x, shape_logical->x + shape_logical->width * (n_chars - 1)); + logical_rect->width = MAX (shape_logical->width, shape_logical->width + shape_logical->width * (n_chars - 1)); + logical_rect->y = shape_logical->y; + logical_rect->height = shape_logical->height; + } + } + else + { + if (ink_rect) + { + ink_rect->x = 0; + ink_rect->y = 0; + ink_rect->width = 0; + ink_rect->height = 0; + } + + if (logical_rect) + { + logical_rect->x = 0; + logical_rect->y = 0; + logical_rect->width = 0; + logical_rect->height = 0; + } + } +} + /** * pango_run_get_extents: * @run: a `PangoRun` @@ -91,8 +140,12 @@ pango_run_get_extents (PangoRun *run, if (!logical_rect && (has_underline || has_overline || has_strikethrough)) logical_rect = &logical; - pango_glyph_string_extents (glyph_item->glyphs, glyph_item->item->analysis.font, - ink_rect, logical_rect); + if (properties.shape) + pango_shape_get_extents (glyph_item->item->num_chars, properties.shape, + ink_rect, logical_rect); + else + pango_glyph_string_extents (glyph_item->glyphs, glyph_item->item->analysis.font, + ink_rect, logical_rect); if (ink_rect && (has_underline || has_overline || has_strikethrough)) { diff --git a/pango/serializer.c b/pango/serializer.c index 6298f48c..f9941faa 100644 --- a/pango/serializer.c +++ b/pango/serializer.c @@ -936,6 +936,7 @@ attr_for_type (GtkJsonParser *parser, switch (type) { + case PANGO_ATTR_SHAPE: default: g_assert_not_reached (); -- cgit v1.2.1