diff options
author | Matthias Clasen <mclasen@redhat.com> | 2021-09-01 11:41:53 +0000 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2021-09-01 11:41:53 +0000 |
commit | c1009a25f561f94bb71f78fe9353ed6c4238e3a7 (patch) | |
tree | a31d52c31aad9b5f6246591c4ede604631b8752e | |
parent | 44e5d0b2d435800516547be3c52af25190a918a5 (diff) | |
parent | a0cb9d5e75a78918ecbf9b71194d8cf229fd3aa7 (diff) | |
download | pango-c1009a25f561f94bb71f78fe9353ed6c4238e3a7.tar.gz |
Merge branch 'baseline-shift' into 'main'
Implement baseline shifts
See merge request GNOME/pango!462
-rw-r--r-- | pango/ellipsize.c | 3 | ||||
-rw-r--r-- | pango/itemize.c | 181 | ||||
-rw-r--r-- | pango/pango-attributes.c | 56 | ||||
-rw-r--r-- | pango/pango-attributes.h | 31 | ||||
-rw-r--r-- | pango/pango-glyph-item.c | 9 | ||||
-rw-r--r-- | pango/pango-glyph-item.h | 11 | ||||
-rw-r--r-- | pango/pango-layout-private.h | 5 | ||||
-rw-r--r-- | pango/pango-layout.c | 201 | ||||
-rw-r--r-- | pango/pango-markup.c | 58 | ||||
-rw-r--r-- | pango/pango-renderer.c | 29 | ||||
-rw-r--r-- | tests/markups/valid-4.expected | 8 | ||||
-rw-r--r-- | tests/test-itemize.c | 2 | ||||
-rw-r--r-- | tests/test-layout.c | 190 |
13 files changed, 515 insertions, 269 deletions
diff --git a/pango/ellipsize.c b/pango/ellipsize.c index e05277f6..d7b7fee0 100644 --- a/pango/ellipsize.c +++ b/pango/ellipsize.c @@ -316,9 +316,8 @@ shape_ellipsis (EllipsizeState *state) */ if (!state->ellipsis_run) { - state->ellipsis_run = g_slice_new (PangoGlyphItem); + state->ellipsis_run = g_slice_new0 (PangoGlyphItem); state->ellipsis_run->glyphs = pango_glyph_string_new (); - state->ellipsis_run->item = NULL; } if (state->ellipsis_run->item) diff --git a/pango/itemize.c b/pango/itemize.c index 11bc2513..29a1cdff 100644 --- a/pango/itemize.c +++ b/pango/itemize.c @@ -34,6 +34,7 @@ #include "pango-attributes-private.h" #include "pango-item-private.h" +#include <hb-ot.h> /* {{{ Font cache */ @@ -1019,6 +1020,169 @@ itemize_state_finish (ItemizeState *state) if (state->base_font) g_object_unref (state->base_font); } + +/* }}} */ +/* {{{ Post-processing */ + +typedef struct { + PangoAttribute *attr; + double scale; +} ScaleItem; + +static gboolean +collect_font_scale (PangoContext *context, + GList **stack, + PangoItem *item, + PangoItem *prev, + double *scale) +{ + gboolean retval = FALSE; + GList *l; + + for (GSList *l = item->analysis.extra_attrs; l; l = l->next) + { + PangoAttribute *attr = l->data; + + if (attr->klass->type == PANGO_ATTR_FONT_SCALE) + { + if (attr->start_index == item->offset) + { + ScaleItem *entry; + hb_font_t *hb_font; + int y_scale; + hb_position_t y_size; + + entry = g_new (ScaleItem, 1); + entry->attr = attr; + *stack = g_list_prepend (*stack, entry); + + hb_font = pango_font_get_hb_font (prev->analysis.font); + hb_font_get_scale (hb_font, NULL, &y_scale); + + switch (((PangoAttrInt *)attr)->value) + { + case PANGO_FONT_SCALE_NONE: + break; + case PANGO_FONT_SCALE_SUPERSCRIPT: + if (hb_ot_metrics_get_position (hb_font, + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE, + &y_size)) + entry->scale = y_size / (double) y_scale; + else + entry->scale = 1 / 1.2; + break; + case PANGO_FONT_SCALE_SUBSCRIPT: + if (hb_ot_metrics_get_position (hb_font, + HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE, + &y_size)) + entry->scale = y_size / (double) y_scale; + else + entry->scale = 1 / 1.2; + break; + default: + g_assert_not_reached (); + } + } + } + } + + *scale = 1.0; + + for (l = *stack; l; l = l->next) + { + ScaleItem *entry = l->data; + *scale *= entry->scale; + retval = TRUE; + } + + l = *stack; + while (l) + { + ScaleItem *entry = l->data; + GList *next = l->next; + + if (entry->attr->end_index == item->offset + item->length) + { + *stack = g_list_delete_link (*stack, l); + g_free (entry); + } + + l = next; + } + + return retval; +} + +static void +apply_scale_to_item (PangoContext *context, + PangoItem *item, + double scale) +{ + PangoFontDescription *desc; + double size; + + desc = pango_font_describe (item->analysis.font); + size = scale * pango_font_description_get_size (desc); + + if (pango_font_description_get_size_is_absolute (desc)) + pango_font_description_set_absolute_size (desc, size); + else + pango_font_description_set_size (desc, size); + + g_object_unref (item->analysis.font); + item->analysis.font = pango_font_map_load_font (context->font_map, context, desc); + + pango_font_description_free (desc); +} + +static void +apply_font_scale (PangoContext *context, + GList *items) +{ + PangoItem *prev; + GList *stack = NULL; + + for (GList *l = items; l; l = l->next) + { + PangoItem *item = l->data; + double scale; + + if (collect_font_scale (context, &stack, item, prev, &scale)) + apply_scale_to_item (context, item, scale); + + prev = item; + } + + if (stack != NULL) + { + g_warning ("Leftover font scales"); + g_list_free_full (stack, g_free); + } +} + +static GList * +post_process_items (PangoContext *context, + GList *items) +{ + items = g_list_reverse (items); + + /* Compute the char offset for each item */ + { + int char_offset = 0; + for (GList *l = items; l; l = l->next) + { + PangoItemPrivate *item = l->data; + item->char_offset = char_offset; + char_offset += item->num_chars; + } + } + + /* apply font-scale */ + apply_font_scale (context, items); + + return items; +} + /* }}} */ /* {{{ Public API */ @@ -1034,8 +1198,6 @@ pango_itemize_with_font (PangoContext *context, const PangoFontDescription *desc) { ItemizeState state; - GList *items; - int char_offset; if (length == 0 || g_utf8_get_char (text + start_index) == '\0') return NULL; @@ -1049,18 +1211,7 @@ pango_itemize_with_font (PangoContext *context, itemize_state_finish (&state); - items = g_list_reverse (state.result); - - /* Compute the char offset for each item */ - char_offset = 0; - for (GList *l = items; l; l = l->next) - { - PangoItemPrivate *item = l->data; - item->char_offset = char_offset; - char_offset += item->num_chars; - } - - return items; + return post_process_items (context, state.result); } /** @@ -1154,6 +1305,6 @@ pango_itemize (PangoContext *context, NULL); } -/* }}} */ + /* }}} */ /* vim:set foldmethod=marker expandtab: */ diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c index 326234d2..65af8f3b 100644 --- a/pango/pango-attributes.c +++ b/pango/pango-attributes.c @@ -924,6 +924,44 @@ pango_attr_rise_new (int rise) } /** + * pango_attr_baseline_shift_new: + * @shift: either a `PangoBaselineShift` enumeration value or an absolute value (> 1024) + * in Pango units, relative to the baseline of the previous run. + * Positive values displace the text upwards. + * + * Create a new baseline displacement attribute. + * + * Return value: (transfer full): the newly allocated + * `PangoAttribute`, which should be freed with + * [method@Pango.Attribute.destroy] + */ +PangoAttribute * +pango_attr_baseline_shift_new (int rise) +{ + static const PangoAttrClass klass = { + PANGO_ATTR_BASELINE_SHIFT, + pango_attr_int_copy, + pango_attr_int_destroy, + pango_attr_int_equal + }; + + return pango_attr_int_new (&klass, (int)rise); +} + +PangoAttribute * +pango_attr_font_scale_new (PangoFontScale scale) +{ + static const PangoAttrClass klass = { + PANGO_ATTR_FONT_SCALE, + pango_attr_int_copy, + pango_attr_int_destroy, + pango_attr_int_equal + }; + + return pango_attr_int_new (&klass, (int)scale); +} + +/** * pango_attr_scale_new: * @scale_factor: factor to scale the font * @@ -1533,6 +1571,8 @@ pango_attribute_as_int (PangoAttribute *attr) case PANGO_ATTR_TEXT_TRANSFORM: case PANGO_ATTR_WORD: case PANGO_ATTR_SENTENCE: + case PANGO_ATTR_BASELINE_SHIFT: + case PANGO_ATTR_FONT_SCALE: return (PangoAttrInt *)attr; default: @@ -2822,10 +2862,14 @@ pango_attr_iterator_get_font (PangoAttrIterator *iterator, { gboolean found = FALSE; - /* Hack: special-case FONT_FEATURES. We don't want them to - * override each other, so we never merge them. This should - * be fixed when we implement attr-merging. */ - if (attr->klass->type != PANGO_ATTR_FONT_FEATURES) + /* Hack: special-case FONT_FEATURES, BASELINE_SHIFT and FONT_SCALE. + * We don't want these to accumulate, not override each other, + * so we never merge them. + * This needs to be handled more systematically. + */ + if (attr->klass->type != PANGO_ATTR_FONT_FEATURES && + attr->klass->type != PANGO_ATTR_BASELINE_SHIFT && + attr->klass->type != PANGO_ATTR_FONT_SCALE) { GSList *tmp_list = *extra_attrs; while (tmp_list) @@ -2892,7 +2936,9 @@ pango_attr_iterator_get_attrs (PangoAttrIterator *iterator) GSList *tmp_list2; gboolean found = FALSE; - if (attr->klass->type != PANGO_ATTR_FONT_DESC) + if (attr->klass->type != PANGO_ATTR_FONT_DESC && + attr->klass->type != PANGO_ATTR_BASELINE_SHIFT && + attr->klass->type != PANGO_ATTR_FONT_SCALE) for (tmp_list2 = attrs; tmp_list2; tmp_list2 = tmp_list2->next) { PangoAttribute *old_attr = tmp_list2->data; diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h index 613aa021..59183a60 100644 --- a/pango/pango-attributes.h +++ b/pango/pango-attributes.h @@ -79,6 +79,8 @@ typedef struct _PangoAttrFontFeatures PangoAttrFontFeatures; * @PANGO_ATTR_ABSOLUTE_LINE_HEIGHT: line height ([struct@Pango.AttrInt]). Since: 1.50 * @PANGO_ATTR_WORD: override segmentation to classify the range of the attribute as a single word ([struct@Pango.AttrInt]). Since 1.50 * @PANGO_ATTR_SENTENCE: override segmentation to classify the range of the attribute as a single sentence ([struct@Pango.AttrInt]). Since 1.50 + * @PANGO_ATTR_BASELINE_SHIFT: baseline displacement ([struct@Pango.AttrInt]). Since 1.50 + * @PANGO_ATTR_FONT_SCALE: font-relative size change ([struct@Pango.AttrInt]). Since 1.50 * * The `PangoAttrType` distinguishes between different types of attributes. * @@ -125,6 +127,8 @@ typedef enum PANGO_ATTR_TEXT_TRANSFORM, /* PangoAttrInt */ PANGO_ATTR_WORD, /* PangoAttrInt */ PANGO_ATTR_SENTENCE, /* PangoAttrInt */ + PANGO_ATTR_BASELINE_SHIFT, /* PangoAttrSize */ + PANGO_ATTR_FONT_SCALE, /* PangoAttrInt */ } PangoAttrType; /** @@ -227,6 +231,29 @@ typedef enum { } PangoTextTransform; /** + * PangoBaselineShift: + * @PANGO_BASELINE_SHIFT_SUPERSCRIPT: Shift the baseline to the superscript position, + * relative to the previous run + * @PANGO_BASELINE_SHIFT_SUBSCRIPT: Shift the baseline to the subscript position, + * relative to the previous run + * + * An enumeration that affects baseline shifts between runs. + * + * Since: 1.50 + */ +typedef enum { + PANGO_BASELINE_SHIFT_NONE, + PANGO_BASELINE_SHIFT_SUPERSCRIPT, + PANGO_BASELINE_SHIFT_SUBSCRIPT, +} PangoBaselineShift; + +typedef enum { + PANGO_FONT_SCALE_NONE, + PANGO_FONT_SCALE_SUPERSCRIPT, + PANGO_FONT_SCALE_SUBSCRIPT, +} PangoFontScale; + +/** * PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING: * * Value for @start_index in `PangoAttribute` that indicates @@ -515,6 +542,10 @@ PangoAttribute * pango_attr_strikethrough_color_new (guint16 guint16 blue); PANGO_AVAILABLE_IN_ALL PangoAttribute * pango_attr_rise_new (int rise); +PANGO_AVAILABLE_IN_1_50 +PangoAttribute * pango_attr_baseline_shift_new (int shift); +PANGO_AVAILABLE_IN_1_50 +PangoAttribute * pango_attr_font_scale_new (PangoFontScale scale); PANGO_AVAILABLE_IN_ALL PangoAttribute * pango_attr_scale_new (double scale_factor); PANGO_AVAILABLE_IN_1_4 diff --git a/pango/pango-glyph-item.c b/pango/pango-glyph-item.c index 5e6ca7b6..7eb1737d 100644 --- a/pango/pango-glyph-item.c +++ b/pango/pango-glyph-item.c @@ -129,6 +129,10 @@ pango_glyph_item_split (PangoGlyphItem *orig, pango_glyph_string_set_size (orig->glyphs, orig->glyphs->num_glyphs - num_glyphs); + new->y_offset = orig->y_offset; + new->start_x_offset = orig->start_x_offset; + new->end_x_offset = -orig->start_x_offset; + return new; } @@ -154,6 +158,9 @@ pango_glyph_item_copy (PangoGlyphItem *orig) result->item = pango_item_copy (orig->item); result->glyphs = pango_glyph_string_copy (orig->glyphs); + result->y_offset = orig->y_offset; + result->start_x_offset = orig->start_x_offset; + result->end_x_offset = orig->end_x_offset; return result; } @@ -196,7 +203,7 @@ G_DEFINE_BOXED_TYPE (PangoGlyphItem, pango_glyph_item, * Since: 1.22 */ PangoGlyphItemIter * -pango_glyph_item_iter_copy (PangoGlyphItemIter *orig) +pango_glyph_item_iter_copy (PangoGlyphItemIter *orig) { PangoGlyphItemIter *result; diff --git a/pango/pango-glyph-item.h b/pango/pango-glyph-item.h index 6c2f9249..fd8951d2 100644 --- a/pango/pango-glyph-item.h +++ b/pango/pango-glyph-item.h @@ -33,6 +33,12 @@ G_BEGIN_DECLS * PangoGlyphItem: * @item: corresponding `PangoItem` * @glyphs: corresponding `PangoGlyphString` + * @y_offset: shift of the baseline, relative to the baseline + * of the containing line. Positive values shift upwards + * @start_x_offset: horizontal displacement to apply before the + * glyph item. Positive values shift right + * @end_x_offset: horizontal displacement to apply after th + * glyph item. Positive values shift right * * A `PangoGlyphItem` is a pair of a `PangoItem` and the glyphs * resulting from shaping the items text. @@ -45,8 +51,11 @@ typedef struct _PangoGlyphItem PangoGlyphItem; struct _PangoGlyphItem { - PangoItem *item; + PangoItem *item; PangoGlyphString *glyphs; + int y_offset; + int start_x_offset; + int end_x_offset; }; #define PANGO_TYPE_GLYPH_ITEM (pango_glyph_item_get_type ()) diff --git a/pango/pango-layout-private.h b/pango/pango-layout-private.h index 1805e730..b9f9b137 100644 --- a/pango/pango-layout-private.h +++ b/pango/pango-layout-private.h @@ -112,11 +112,12 @@ struct _PangoLayoutIter Extents *line_extents; int line_index; - /* X position of the current run */ + /* Position of the current run */ int run_x; - /* Width of the current run */ + /* Width and end offset of the current run */ int run_width; + int end_x_offset; /* this run is left-to-right */ gboolean ltr; diff --git a/pango/pango-layout.c b/pango/pango-layout.c index d3d93e60..098e3a3e 100644 --- a/pango/pango-layout.c +++ b/pango/pango-layout.c @@ -92,7 +92,7 @@ typedef struct _ItemProperties ItemProperties; typedef struct _ParaBreakState ParaBreakState; -/* Note that rise, letter_spacing, shape are constant across items, +/* Note that letter_spacing and shape are constant across items, * since we pass them into itemization. * * uline and strikethrough can vary across an item, so we collect @@ -108,7 +108,6 @@ struct _ItemProperties guint uline_error : 1; guint strikethrough : 1; guint oline_single : 1; - gint rise; gint letter_spacing; gboolean shape_set; PangoRectangle *shape_ink_rect; @@ -3621,6 +3620,8 @@ struct _ParaBreakState int remaining_width; /* Amount of space remaining on line; < 0 is infinite */ int hyphen_width; /* How much space a hyphen will take */ + + GList *baseline_shifts; }; static gboolean @@ -4320,10 +4321,12 @@ affects_itemization (PangoAttribute *attr, case PANGO_ATTR_ABSOLUTE_SIZE: case PANGO_ATTR_GRAVITY: case PANGO_ATTR_GRAVITY_HINT: + case PANGO_ATTR_FONT_SCALE: /* These need to be constant across runs */ case PANGO_ATTR_LETTER_SPACING: case PANGO_ATTR_SHAPE: case PANGO_ATTR_RISE: + case PANGO_ATTR_BASELINE_SHIFT: case PANGO_ATTR_LINE_HEIGHT: case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT: case PANGO_ATTR_TEXT_TRANSFORM: @@ -4485,6 +4488,7 @@ pango_layout_check_lines (PangoLayout *layout) state.log_widths = NULL; state.num_log_widths = 0; + state.baseline_shifts = NULL; do { @@ -4593,6 +4597,7 @@ pango_layout_check_lines (PangoLayout *layout) while (!done); g_free (state.log_widths); + g_list_free_full (state.baseline_shifts, g_free); apply_attributes_to_runs (layout, attrs); layout->lines = g_slist_reverse (layout->lines); @@ -5220,6 +5225,7 @@ pango_layout_run_get_extents_and_height (PangoLayoutRun *run, PangoFontMetrics *metrics = NULL; gboolean has_underline; gboolean has_overline; + int y_offset; if (G_UNLIKELY (!run_ink && !run_logical && !line_logical && !height)) return; @@ -5313,6 +5319,8 @@ pango_layout_run_get_extents_and_height (PangoLayoutRun *run, *height = pango_font_metrics_get_height (metrics); } + y_offset = run->y_offset; + if (run->item->analysis.flags & PANGO_ANALYSIS_FLAG_CENTERED_BASELINE) { gboolean is_hinted = (run_logical->y & run_logical->height & (PANGO_SCALE - 1)) == 0; @@ -5321,17 +5329,14 @@ pango_layout_run_get_extents_and_height (PangoLayoutRun *run, if (is_hinted) adjustment = PANGO_UNITS_ROUND (adjustment); - properties.rise += adjustment; + y_offset += adjustment; } - if (properties.rise != 0) - { - if (run_ink) - run_ink->y -= properties.rise; + if (run_ink) + run_ink->y -= y_offset; - if (run_logical) - run_logical->y -= properties.rise; - } + if (run_logical) + run_logical->y -= y_offset; if (line_logical) { @@ -6147,6 +6152,149 @@ justify_words (PangoLayoutLine *line, state->remaining_width -= added_so_far; } +typedef struct { + PangoAttribute *attr; + int x_offset; + int y_offset; +} BaselineItem; + +static void +collect_baseline_shift (ParaBreakState *state, + PangoItem *item, + PangoItem *prev, + int *start_x_offset, + int *start_y_offset, + int *end_x_offset, + int *end_y_offset) +{ + *start_x_offset = 0; + *start_y_offset = 0; + *end_x_offset = 0; + *end_y_offset = 0; + + for (GSList *l = item->analysis.extra_attrs; l; l = l->next) + { + PangoAttribute *attr = l->data; + + if (attr->klass->type == PANGO_ATTR_RISE) + { + int value = ((PangoAttrInt *)attr)->value; + + *start_y_offset += value; + *end_y_offset -= value; + } + else if (attr->klass->type == PANGO_ATTR_BASELINE_SHIFT) + { + if (attr->start_index == item->offset) + { + BaselineItem *entry; + int value; + + entry = g_new0 (BaselineItem, 1); + entry->attr = attr; + state->baseline_shifts = g_list_prepend (state->baseline_shifts, entry); + + value = ((PangoAttrInt *)attr)->value; + + if (value > 1024 || value < -1024) + { + entry->y_offset = value; + /* FIXME: compute an x_offset from value to italic angle */ + } + else + { + int superscript_x_offset = 0; + int superscript_y_offset = 0; + int subscript_x_offset = 0; + int subscript_y_offset = 0; + + + if (prev) + { + hb_font_t *hb_font = pango_font_get_hb_font (prev->analysis.font); + hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET, &superscript_y_offset); + hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET, &superscript_x_offset); + hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET, &subscript_y_offset); + hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET, &subscript_x_offset); + } + + if (superscript_y_offset == 0) + superscript_y_offset = 5000; + if (subscript_y_offset == 0) + subscript_y_offset = 5000; + + switch (value) + { + case PANGO_BASELINE_SHIFT_NONE: + entry->x_offset = 0; + entry->y_offset = 0; + break; + case PANGO_BASELINE_SHIFT_SUPERSCRIPT: + entry->x_offset = superscript_x_offset; + entry->y_offset = superscript_y_offset; + break; + case PANGO_BASELINE_SHIFT_SUBSCRIPT: + entry->x_offset = subscript_x_offset; + entry->y_offset = -subscript_y_offset; + break; + default: + g_assert_not_reached (); + } + } + + *start_x_offset += entry->x_offset; + *start_y_offset += entry->y_offset; + } + + if (attr->end_index == item->offset + item->length) + { + BaselineItem *entry = state->baseline_shifts->data; + + if (attr->start_index == entry->attr->start_index && + attr->end_index == entry->attr->end_index && + ((PangoAttrInt *)attr)->value == ((PangoAttrInt *)entry->attr)->value) + { + *end_x_offset -= entry->x_offset; + *end_y_offset -= entry->y_offset; + } + else + g_warning ("Baseline attributes mismatch\n"); + + state->baseline_shifts = g_list_remove (state->baseline_shifts, entry); + g_free (entry); + } + } + } +} + +static void +apply_baseline_shift (PangoLayoutLine *line, + ParaBreakState *state) +{ + int y_offset = 0; + PangoItem *prev = NULL; + + for (GSList *l = line->runs; l; l = l->next) + { + PangoLayoutRun *run = l->data; + PangoItem *item = run->item; + int start_x_offset, end_x_offset; + int start_y_offset, end_y_offset; + + collect_baseline_shift (state, item, prev, &start_x_offset, &start_y_offset, &end_x_offset, &end_y_offset); + + y_offset += start_y_offset; + + run->y_offset = y_offset; + run->start_x_offset = start_x_offset; + run->end_x_offset = end_x_offset; + + y_offset += end_y_offset; + + prev = item; + } +} + static void pango_layout_line_postprocess (PangoLayoutLine *line, ParaBreakState *state, @@ -6167,6 +6315,8 @@ pango_layout_line_postprocess (PangoLayoutLine *line, */ line->runs = g_slist_reverse (line->runs); + apply_baseline_shift (line, state); + /* Ellipsize the line if necessary */ if (G_UNLIKELY (state->line_width >= 0 && @@ -6224,7 +6374,6 @@ pango_layout_get_item_properties (PangoItem *item, properties->oline_single = FALSE; properties->strikethrough = FALSE; properties->letter_spacing = 0; - properties->rise = 0; properties->shape_set = FALSE; properties->shape_ink_rect = NULL; properties->shape_logical_rect = NULL; @@ -6279,10 +6428,6 @@ pango_layout_get_item_properties (PangoItem *item, properties->strikethrough = ((PangoAttrInt *)attr)->value; break; - case PANGO_ATTR_RISE: - properties->rise = ((PangoAttrInt *)attr)->value; - break; - case PANGO_ATTR_LETTER_SPACING: properties->letter_spacing = ((PangoAttrInt *)attr)->value; break; @@ -6418,16 +6563,22 @@ update_run (PangoLayoutIter *iter, if (iter->run_list_link == iter->line->runs) iter->run_x = line_ext->logical_rect.x; else - iter->run_x += iter->run_width; + { + iter->run_x += iter->end_x_offset + iter->run_width; + if (iter->run) + iter->run_x += iter->run->start_x_offset; + } if (iter->run) { iter->run_width = pango_glyph_string_get_width (iter->run->glyphs); + iter->end_x_offset = iter->run->end_x_offset; } else { /* The empty run at the end of a line */ iter->run_width = 0; + iter->end_x_offset = 0; } if (iter->run) @@ -7110,8 +7261,6 @@ pango_layout_iter_get_cluster_extents (PangoLayoutIter *iter, PangoRectangle *ink_rect, PangoRectangle *logical_rect) { - ItemProperties properties; - if (ITER_IS_INVALID (iter)) return; @@ -7124,8 +7273,6 @@ pango_layout_iter_get_cluster_extents (PangoLayoutIter *iter, return; } - pango_layout_get_item_properties (iter->run->item, &properties); - pango_glyph_string_extents_range (iter->run->glyphs, iter->cluster_start, iter->next_cluster_glyph, @@ -7135,16 +7282,16 @@ pango_layout_iter_get_cluster_extents (PangoLayoutIter *iter, if (ink_rect) { - ink_rect->x += iter->cluster_x; - ink_rect->y -= properties.rise; + ink_rect->x += iter->cluster_x + iter->run->start_x_offset; + ink_rect->y -= iter->run->y_offset; offset_y (iter, &ink_rect->y); } if (logical_rect) { g_assert (logical_rect->width == iter->cluster_width); - logical_rect->x += iter->cluster_x; - logical_rect->y -= properties.rise; + logical_rect->x += iter->cluster_x + iter->run->start_x_offset; + logical_rect->y -= iter->run->y_offset; offset_y (iter, &logical_rect->y); } } @@ -7325,17 +7472,13 @@ pango_layout_iter_get_baseline (PangoLayoutIter *iter) int pango_layout_iter_get_run_baseline (PangoLayoutIter *iter) { - ItemProperties properties; - if (ITER_IS_INVALID (iter)) return 0; if (!iter->run) return iter->line_extents[iter->line_index].baseline; - pango_layout_get_item_properties (iter->run->item, &properties); - - return iter->line_extents[iter->line_index].baseline - properties.rise; + return iter->line_extents[iter->line_index].baseline - iter->run->y_offset; } /** diff --git a/pango/pango-markup.c b/pango/pango-markup.c index a9df8ed0..791e71fd 100644 --- a/pango/pango-markup.c +++ b/pango/pango-markup.c @@ -1217,6 +1217,7 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, const char *strikethrough = NULL; const char *strikethrough_color = NULL; const char *rise = NULL; + const char *baseline_shift = NULL; const char *letter_spacing = NULL; const char *lang = NULL; const char *fallback = NULL; @@ -1231,6 +1232,7 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, const char *line_height = NULL; const char *text_transform = NULL; const char *segment = NULL; + const char *font_scale = NULL; g_markup_parse_context_get_position (context, &line_number, &char_number); @@ -1268,6 +1270,7 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, CHECK_ATTRIBUTE2(background, "bgcolor"); CHECK_ATTRIBUTE (background_alpha); CHECK_ATTRIBUTE2(background_alpha, "bgalpha"); + CHECK_ATTRIBUTE(baseline_shift); break; case 'c': CHECK_ATTRIBUTE2(foreground, "color"); @@ -1284,6 +1287,7 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, CHECK_ATTRIBUTE2(style, "font_style"); CHECK_ATTRIBUTE2(variant, "font_variant"); CHECK_ATTRIBUTE2(weight, "font_weight"); + CHECK_ATTRIBUTE(font_scale); CHECK_ATTRIBUTE (foreground); CHECK_ATTRIBUTE2(foreground, "fgcolor"); @@ -1675,6 +1679,38 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, add_attribute (tag, pango_attr_rise_new (n)); } + if (G_UNLIKELY (baseline_shift)) + { + gint shift = 0; + + if (span_parse_enum ("baseline_shift", baseline_shift, PANGO_TYPE_BASELINE_SHIFT, (int*)(void*)&shift, line_number, NULL)) + add_attribute (tag, pango_attr_baseline_shift_new (shift)); + else if (parse_length (baseline_shift, &shift) && (shift > 1024 || shift < -1024)) + add_attribute (tag, pango_attr_baseline_shift_new (shift)); + else + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + _("Value of 'baseline_shift' attribute on <span> tag on line %d " + "could not be parsed; should be 'superscript' or 'subscript' or " + "an integer, or a string such as '5.5pt', not '%s'"), + line_number, baseline_shift); + goto error; + } + + } + + if (G_UNLIKELY (font_scale)) + { + PangoFontScale scale; + + if (!span_parse_enum ("font_scale", font_scale, PANGO_TYPE_FONT_SCALE, (int*)(void*)&scale, line_number, error)) + goto error; + + add_attribute (tag, pango_attr_font_scale_new (scale)); + } + if (G_UNLIKELY (letter_spacing)) { gint n = 0; @@ -1797,8 +1833,6 @@ s_parse_func (MarkupData *md G_GNUC_UNUSED, return TRUE; } -#define SUPERSUB_RISE 5000 - static gboolean sub_parse_func (MarkupData *md G_GNUC_UNUSED, OpenTag *tag, @@ -1809,14 +1843,8 @@ sub_parse_func (MarkupData *md G_GNUC_UNUSED, { CHECK_NO_ATTRS("sub"); - /* Shrink font, and set a negative rise */ - if (tag) - { - tag->scale_level_delta -= 1; - tag->scale_level -= 1; - } - - add_attribute (tag, pango_attr_rise_new (-SUPERSUB_RISE)); + add_attribute (tag, pango_attr_font_scale_new (PANGO_FONT_SCALE_SUBSCRIPT)); + add_attribute (tag, pango_attr_baseline_shift_new (PANGO_BASELINE_SHIFT_SUBSCRIPT)); return TRUE; } @@ -1831,14 +1859,8 @@ sup_parse_func (MarkupData *md G_GNUC_UNUSED, { 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)); + add_attribute (tag, pango_attr_font_scale_new (PANGO_FONT_SCALE_SUPERSCRIPT)); + add_attribute (tag, pango_attr_baseline_shift_new (PANGO_BASELINE_SHIFT_SUPERSCRIPT)); return TRUE; } diff --git a/pango/pango-renderer.c b/pango/pango-renderer.c index 055cdd97..d47ba13a 100644 --- a/pango/pango-renderer.c +++ b/pango/pango-renderer.c @@ -502,14 +502,10 @@ add_strikethrough (PangoRenderer *renderer, static void get_item_properties (PangoItem *item, - gint *rise, PangoAttrShape **shape_attr) { GSList *l; - if (rise) - *rise = 0; - if (shape_attr) *shape_attr = NULL; @@ -524,11 +520,6 @@ get_item_properties (PangoItem *item, *shape_attr = (PangoAttrShape *)attr; break; - case PANGO_ATTR_RISE: - if (rise) - *rise = ((PangoAttrInt *)attr)->value; - break; - default: break; } @@ -616,18 +607,18 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer, for (l = line->runs; l; l = l->next) { PangoFontMetrics *metrics; - gint rise; PangoLayoutRun *run = l->data; PangoAttrShape *shape_attr; PangoRectangle ink_rect, *ink = NULL; PangoRectangle logical_rect, *logical = NULL; + int y_off; if (run->item->analysis.flags & PANGO_ANALYSIS_FLAG_CENTERED_BASELINE) logical = &logical_rect; pango_renderer_prepare_run (renderer, run); - get_item_properties (run->item, &rise, &shape_attr); + get_item_properties (run->item, &shape_attr); if (shape_attr) { @@ -660,6 +651,9 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer, state.logical_rect_end = x + x_off + glyph_string_width; + x_off += run->start_x_offset; + y_off = run->y_offset; + if (run->item->analysis.flags & PANGO_ANALYSIS_FLAG_CENTERED_BASELINE) { gboolean is_hinted = ((logical_rect.y | logical_rect.height) & (PANGO_SCALE - 1)) == 0; @@ -668,7 +662,7 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer, if (is_hinted) adjustment = PANGO_UNITS_ROUND (adjustment); - rise += adjustment; + y_off += adjustment; } @@ -690,14 +684,14 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer, if (shape_attr) { - draw_shaped_glyphs (renderer, run->glyphs, shape_attr, x + x_off, y - rise); + draw_shaped_glyphs (renderer, run->glyphs, shape_attr, x + x_off, y - y_off); } else { pango_renderer_draw_glyph_item (renderer, text, run, - x + x_off, y - rise); + x + x_off, y - y_off); } if (renderer->underline != PANGO_UNDERLINE_NONE || @@ -709,17 +703,17 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer, if (renderer->underline != PANGO_UNDERLINE_NONE) add_underline (renderer, &state,metrics, - x + x_off, y - rise, + x + x_off, y - y_off, ink, logical); if (renderer->priv->overline != PANGO_OVERLINE_NONE) add_overline (renderer, &state,metrics, - x + x_off, y - rise, + x + x_off, y - y_off, ink, logical); if (renderer->strikethrough) add_strikethrough (renderer, &state, metrics, - x + x_off, y - rise, + x + x_off, y - y_off, ink, logical, run->glyphs->num_glyphs); pango_font_metrics_unref (metrics); @@ -737,6 +731,7 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer, draw_strikethrough (renderer, &state); x_off += glyph_string_width; + x_off += run->end_x_offset; } /* Finish off any remaining underlines diff --git a/tests/markups/valid-4.expected b/tests/markups/valid-4.expected index 0391ec8e..a9e16728 100644 --- a/tests/markups/valid-4.expected +++ b/tests/markups/valid-4.expected @@ -18,8 +18,8 @@ range 16 29 [16,41]strikethrough=1 range 29 32 [16,41]strikethrough=1 -[29,32]scale=0.833333 -[29,32]rise=-5000 +[29,32]font-scale=2 +[29,32]baseline-shift=2 range 32 33 [16,41]strikethrough=1 range 33 38 @@ -27,8 +27,8 @@ range 33 38 [33,38]scale=0.833333 range 38 41 [16,41]strikethrough=1 -[38,41]scale=0.833333 -[38,41]rise=5000 +[38,41]font-scale=1 +[38,41]baseline-shift=1 range 41 42 range 42 45 [42,54]family=Monospace diff --git a/tests/test-itemize.c b/tests/test-itemize.c index db6a715f..e5775985 100644 --- a/tests/test-itemize.c +++ b/tests/test-itemize.c @@ -73,10 +73,12 @@ affects_itemization (PangoAttribute *attr, case PANGO_ATTR_ABSOLUTE_SIZE: case PANGO_ATTR_GRAVITY: case PANGO_ATTR_GRAVITY_HINT: + case PANGO_ATTR_FONT_SCALE: /* These are part of ItemProperties, so need to break runs */ case PANGO_ATTR_LETTER_SPACING: case PANGO_ATTR_SHAPE: case PANGO_ATTR_RISE: + case PANGO_ATTR_BASELINE_SHIFT: case PANGO_ATTR_LINE_HEIGHT: case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT: case PANGO_ATTR_TEXT_TRANSFORM: diff --git a/tests/test-layout.c b/tests/test-layout.c index 0139c138..cd60450e 100644 --- a/tests/test-layout.c +++ b/tests/test-layout.c @@ -388,14 +388,7 @@ test_file (const char *filename, GString *string) PangoFontDescription *desc; const PangoFontDescription *desc2; guint serial; - PangoRectangle ink_rect, logical_rect; - PangoRectangle ink_rect1, logical_rect1; - int width, height; - int width1, height1; PangoTabArray *tabs; - GSList *lines, *l; - PangoLayoutIter *iter; - PangoLayoutIter *iter2; if (context == NULL) context = pango_font_map_create_context (pango_cairo_font_map_get_default ()); @@ -483,174 +476,6 @@ test_file (const char *filename, GString *string) g_assert_cmpint (pango_layout_get_character_count (layout), ==, g_utf8_strlen (pango_layout_get_text (layout), -1)); - /* Some checks on extents - we have to be careful here, - * since we don't want to depend on font metrics. - */ - pango_layout_get_size (layout, &width, &height); - pango_layout_get_extents (layout, &ink_rect, &logical_rect); - g_assert_cmpint (width, ==, logical_rect.width); - g_assert_cmpint (height, ==, logical_rect.height); - - pango_extents_to_pixels (&ink_rect, NULL); - pango_extents_to_pixels (&logical_rect, NULL); - pango_layout_get_pixel_extents (layout, &ink_rect1, &logical_rect1); - pango_layout_get_pixel_size (layout, &width1, &height1); - - assert_rectangle_equal (&ink_rect, &ink_rect1); - assert_rectangle_equal (&logical_rect, &logical_rect1); - g_assert_cmpint (width1, ==, logical_rect1.width); - g_assert_cmpint (height1, ==, logical_rect1.height); - - lines = pango_layout_get_lines (layout); - for (l = lines; l; l = l->next) - { - PangoLayoutLine *line = l->data; - int line_width; - int line_x; - PangoRectangle line_ink, line_logical; - PangoRectangle line_ink1, line_logical1; - gboolean done; - - pango_layout_line_get_extents (line, &line_ink, &line_logical); - line_x = line_logical.x; - line_width = line_logical.width; - pango_extents_to_pixels (&line_ink, NULL); - pango_extents_to_pixels (&line_logical, NULL); - pango_layout_line_get_pixel_extents (line, &line_ink1, &line_logical1); - - /* Not in layout coordinates, so just compare sizes */ - assert_rectangle_size_contained (&line_ink, &ink_rect); - assert_rectangle_size_contained (&line_logical, &logical_rect); - assert_rectangle_size_contained (&line_ink1, &ink_rect1); - assert_rectangle_size_contained (&line_logical1, &logical_rect1); - - if (pango_layout_is_ellipsized (layout)) - continue; - - /* FIXME: should have a way to position iters */ - iter = pango_layout_get_iter (layout); - while (pango_layout_iter_get_line_readonly (iter) != line) - pango_layout_iter_next_line (iter); - - done = FALSE; - while (!done && pango_layout_iter_get_line_readonly (iter) == line) - { - int prev_index, index, next_index; - int x; - int *ranges; - int n_ranges; - gboolean found_range; - PangoLayoutRun *run; - - index = pango_layout_iter_get_index (iter); - run = pango_layout_iter_get_run_readonly (iter); - - if (!pango_layout_iter_next_cluster (iter)) - done = TRUE; - - pango_layout_line_index_to_x (line, index, 0, &x); - g_assert_cmpint (0, <=, x); - g_assert_cmpint (x, <=, line_width); - - if (!run) - break; - - prev_index = run->item->offset; - next_index = run->item->offset + run->item->length; - - { - PangoGlyphItem *run2 = pango_glyph_item_copy (run); - g_assert_cmpint (run2->item->offset, ==, run->item->offset); - g_assert_cmpint (run2->item->length, ==, run->item->length); - pango_glyph_item_free (run2); - } - - pango_layout_line_get_x_ranges (line, prev_index, next_index, &ranges, &n_ranges); - - /* The index is within the run, so the x should be in one of the ranges */ - if (n_ranges > 0) - { - found_range = FALSE; - for (int k = 0; k < n_ranges; k++) - { - if (x + line_x >= ranges[2*k] && x + line_x <= ranges[2*k + 1]) - { - found_range = TRUE; - break; - } - } - } - - g_assert_true (found_range); - g_free (ranges); - } - - pango_layout_iter_free (iter); - } - - iter = pango_layout_get_iter (layout); - g_assert_true (pango_layout_iter_get_layout (iter) == layout); - g_assert_cmpint (pango_layout_iter_get_index (iter), ==, 0); - pango_layout_iter_get_layout_extents (iter, &ink_rect, &logical_rect); - - iter2 = pango_layout_iter_copy (iter); - - do - { - PangoRectangle line_ink, line_logical; - int baseline; - PangoLayoutLine *line; - PangoLayoutRun *run; - - line = pango_layout_iter_get_line (iter); - - pango_layout_iter_get_line_extents (iter, &line_ink, &line_logical); - baseline = pango_layout_iter_get_baseline (iter); - - assert_rectangle_contained (&line_ink, &ink_rect); - assert_rectangle_contained (&line_logical, &logical_rect); - - g_assert_cmpint (line_logical.y, <=, baseline); - g_assert_cmpint (baseline, <=, line_logical.y + line_logical.height); - - if (pango_layout_iter_get_index (iter) == pango_layout_iter_get_index (iter2)) - { - g_assert_cmpint (baseline, ==, pango_layout_get_baseline (layout)); - g_assert_true (line->is_paragraph_start); - } - - if (pango_layout_iter_at_last_line (iter)) - { - g_assert_cmpint (line->start_index + line->length, <=, strlen (pango_layout_get_text (layout))); - } - - run = pango_layout_iter_get_run (iter); - - if (run) - { - const char *text; - int *widths; - int *widths2; - - text = pango_layout_get_text (layout); - - widths = g_new (int, run->item->num_chars); - pango_glyph_item_get_logical_widths (run, text, widths); - - widths2 = g_new (int, run->item->num_chars); - pango_glyph_string_get_logical_widths (run->glyphs, text + run->item->offset, run->item->length, run->item->analysis.level, widths2); - - g_assert_true (memcmp (widths, widths2, sizeof (int) * run->item->num_chars) == 0); - - g_free (widths); - g_free (widths2); - } - } - while (pango_layout_iter_next_line (iter)); - - pango_layout_iter_free (iter); - pango_layout_iter_free (iter2); - /* generate the dumps */ g_string_append (string, pango_layout_get_text (layout)); @@ -792,9 +617,24 @@ main (int argc, char *argv[]) GError *error = NULL; const gchar *name; gchar *path; + GOptionContext *option_context; + GOptionEntry entries[] = { + { "show-fonts", '0', 0, G_OPTION_ARG_NONE, &opt_show_font, "Print font names in dumps", NULL }, + { NULL, 0 }, + }; setlocale (LC_ALL, ""); + option_context = g_option_context_new (""); + g_option_context_add_main_entries (option_context, entries, NULL); + g_option_context_set_ignore_unknown_options (option_context, TRUE); + if (!g_option_context_parse (option_context, &argc, &argv, &error)) + { + g_error ("failed to parse options: %s", error->message); + return 1; + } + g_option_context_free (option_context); + if (g_getenv ("PANGO_TEST_SHOW_FONT")) opt_show_font = TRUE; |