[pango/baseline-shift: 2/5] Implement baseline shifts
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pango/baseline-shift: 2/5] Implement baseline shifts
- Date: Mon, 30 Aug 2021 05:35:47 +0000 (UTC)
commit 012b223bcb7df1a8e5ffe6eaf64fc5a3da2548cd
Author: Matthias Clasen <mclasen redhat com>
Date: Sun Aug 29 00:10:03 2021 -0400
Implement baseline shifts
Add a new baseline-shift attribute, which is similar to
rise, but accumulates. In addition, it supports font-
relative values such as superscript and subscript.
We implement support for this by computing baseline
shifts for run during line post-processing, and storing
them in the runs. The renderer now takes these shifts
into account when rendering layout lines.
pango/pango-attributes.c | 25 +++++++
pango/pango-attributes.h | 21 ++++++
pango/pango-glyph-item.c | 5 +-
pango/pango-glyph-item.h | 5 +-
pango/pango-layout.c | 168 +++++++++++++++++++++++++++++++++++++++--------
pango/pango-markup.c | 24 +++++++
pango/pango-renderer.c | 27 +++-----
tests/test-itemize.c | 1 +
8 files changed, 231 insertions(+), 45 deletions(-)
---
diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c
index 326234d2..8507c963 100644
--- a/pango/pango-attributes.c
+++ b/pango/pango-attributes.c
@@ -923,6 +923,30 @@ pango_attr_rise_new (int rise)
return pango_attr_int_new (&klass, (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);
+}
/**
* pango_attr_scale_new:
* @scale_factor: factor to scale the font
@@ -1533,6 +1557,7 @@ pango_attribute_as_int (PangoAttribute *attr)
case PANGO_ATTR_TEXT_TRANSFORM:
case PANGO_ATTR_WORD:
case PANGO_ATTR_SENTENCE:
+ case PANGO_ATTR_BASELINE_SHIFT:
return (PangoAttrInt *)attr;
default:
diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h
index 613aa021..1c9df2c5 100644
--- a/pango/pango-attributes.h
+++ b/pango/pango-attributes.h
@@ -79,6 +79,7 @@ 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.AttrSize]). Since 1.50
*
* The `PangoAttrType` distinguishes between different types of attributes.
*
@@ -125,6 +126,7 @@ typedef enum
PANGO_ATTR_TEXT_TRANSFORM, /* PangoAttrInt */
PANGO_ATTR_WORD, /* PangoAttrInt */
PANGO_ATTR_SENTENCE, /* PangoAttrInt */
+ PANGO_ATTR_BASELINE_SHIFT, /* PangoAttrSize */
} PangoAttrType;
/**
@@ -226,6 +228,23 @@ typedef enum {
PANGO_TEXT_TRANSFORM_CAPITALIZE,
} 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;
+
/**
* PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING:
*
@@ -515,6 +534,8 @@ 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_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..c64bfa13 100644
--- a/pango/pango-glyph-item.c
+++ b/pango/pango-glyph-item.c
@@ -129,6 +129,8 @@ 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;
+
return new;
}
@@ -154,6 +156,7 @@ 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;
return result;
}
@@ -196,7 +199,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..baea69fc 100644
--- a/pango/pango-glyph-item.h
+++ b/pango/pango-glyph-item.h
@@ -33,6 +33,8 @@ G_BEGIN_DECLS
* PangoGlyphItem:
* @item: corresponding `PangoItem`
* @glyphs: corresponding `PangoGlyphString`
+ * @baseline: shift of the baseline, relative to the
+ * containing lines baseline. Positive values shift upwards
*
* A `PangoGlyphItem` is a pair of a `PangoItem` and the glyphs
* resulting from shaping the items text.
@@ -45,8 +47,9 @@ typedef struct _PangoGlyphItem PangoGlyphItem;
struct _PangoGlyphItem
{
- PangoItem *item;
+ PangoItem *item;
PangoGlyphString *glyphs;
+ int y_offset;
};
#define PANGO_TYPE_GLYPH_ITEM (pango_glyph_item_get_type ())
diff --git a/pango/pango-layout.c b/pango/pango-layout.c
index d3d93e60..58e3a91c 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
@@ -4324,6 +4325,7 @@ affects_itemization (PangoAttribute *attr,
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 +4487,7 @@ pango_layout_check_lines (PangoLayout *layout)
state.log_widths = NULL;
state.num_log_widths = 0;
+ state.baseline_shifts = NULL;
do
{
@@ -4593,6 +4596,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 +5224,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 +5318,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 +5328,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 +6151,129 @@ justify_words (PangoLayoutLine *line,
state->remaining_width -= added_so_far;
}
+typedef struct {
+ PangoAttribute *attr;
+ int shift;
+} BaselineItem;
+
+static void
+collect_shifts (ParaBreakState *state,
+ PangoItem *item,
+ PangoItem *prev,
+ int *start_shift,
+ int *end_shift)
+{
+ *start_shift = 0;
+ *end_shift = 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_shift += value;
+ *end_shift -= 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;
+
+ value = ((PangoAttrInt *)attr)->value;
+
+ if (value > 1024 || value < -1024)
+ {
+ entry->shift = value;
+ }
+ else
+ {
+ int superscript_shift = 0;
+ int subscript_shift = 0;
+ hb_font_t *hb_font;
+
+
+ if (prev)
+ {
+ 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_shift);
+ hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET,
&subscript_shift);
+ }
+
+ if (superscript_shift == 0)
+ superscript_shift = 5000;
+ if (subscript_shift == 0)
+ subscript_shift = 5000;
+
+ switch (value)
+ {
+ case PANGO_BASELINE_SHIFT_NONE:
+ entry->shift = 0;
+ break;
+ case PANGO_BASELINE_SHIFT_SUPERSCRIPT:
+ entry->shift = superscript_shift;
+ break;
+ case PANGO_BASELINE_SHIFT_SUBSCRIPT:
+ entry->shift = -subscript_shift;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+
+ *start_shift += entry->shift;
+ state->baseline_shifts = g_list_prepend (state->baseline_shifts, entry);
+ }
+ 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_shift -= entry->shift;
+ else
+ g_warning ("Baseline attributes mismatch\n");
+
+ state->baseline_shifts = g_list_remove (state->baseline_shifts, entry);
+ g_free (entry);
+ }
+ }
+ }
+}
+
+static void
+apply_baseline_shifts (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_y_offset, end_y_offset;
+
+ collect_shifts (state, item, prev, &start_y_offset, &end_y_offset);
+
+ y_offset += start_y_offset;
+
+ run->y_offset = y_offset;
+
+ y_offset += end_y_offset;
+
+ prev = item;
+ }
+}
+
static void
pango_layout_line_postprocess (PangoLayoutLine *line,
ParaBreakState *state,
@@ -6167,6 +6294,8 @@ pango_layout_line_postprocess (PangoLayoutLine *line,
*/
line->runs = g_slist_reverse (line->runs);
+ apply_baseline_shifts (line, state);
+
/* Ellipsize the line if necessary
*/
if (G_UNLIKELY (state->line_width >= 0 &&
@@ -6224,7 +6353,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 +6407,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;
@@ -7110,8 +7234,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 +7246,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,
@@ -7136,7 +7256,7 @@ pango_layout_iter_get_cluster_extents (PangoLayoutIter *iter,
if (ink_rect)
{
ink_rect->x += iter->cluster_x;
- ink_rect->y -= properties.rise;
+ ink_rect->y -= iter->run->y_offset;
offset_y (iter, &ink_rect->y);
}
@@ -7144,7 +7264,7 @@ pango_layout_iter_get_cluster_extents (PangoLayoutIter *iter,
{
g_assert (logical_rect->width == iter->cluster_width);
logical_rect->x += iter->cluster_x;
- logical_rect->y -= properties.rise;
+ logical_rect->y -= iter->run->y_offset;
offset_y (iter, &logical_rect->y);
}
}
@@ -7325,17 +7445,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..65396547 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;
@@ -1268,6 +1269,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");
@@ -1675,6 +1677,28 @@ 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 (letter_spacing))
{
gint n = 0;
diff --git a/pango/pango-renderer.c b/pango/pango-renderer.c
index 055cdd97..231ebe7d 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;
}
@@ -589,6 +580,7 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer,
gboolean got_overall = FALSE;
PangoRectangle overall_rect;
const char *text;
+ int y_off;
g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
@@ -616,7 +608,6 @@ 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;
@@ -627,7 +618,7 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer,
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,8 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer,
state.logical_rect_end = x + x_off + glyph_string_width;
+ 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 +661,7 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer,
if (is_hinted)
adjustment = PANGO_UNITS_ROUND (adjustment);
- rise += adjustment;
+ y_off += adjustment;
}
@@ -690,14 +683,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 +702,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);
diff --git a/tests/test-itemize.c b/tests/test-itemize.c
index db6a715f..29f59210 100644
--- a/tests/test-itemize.c
+++ b/tests/test-itemize.c
@@ -77,6 +77,7 @@ affects_itemization (PangoAttribute *attr,
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:
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]