summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2021-09-01 11:41:53 +0000
committerMatthias Clasen <mclasen@redhat.com>2021-09-01 11:41:53 +0000
commitc1009a25f561f94bb71f78fe9353ed6c4238e3a7 (patch)
treea31d52c31aad9b5f6246591c4ede604631b8752e
parent44e5d0b2d435800516547be3c52af25190a918a5 (diff)
parenta0cb9d5e75a78918ecbf9b71194d8cf229fd3aa7 (diff)
downloadpango-c1009a25f561f94bb71f78fe9353ed6c4238e3a7.tar.gz
Merge branch 'baseline-shift' into 'main'
Implement baseline shifts See merge request GNOME/pango!462
-rw-r--r--pango/ellipsize.c3
-rw-r--r--pango/itemize.c181
-rw-r--r--pango/pango-attributes.c56
-rw-r--r--pango/pango-attributes.h31
-rw-r--r--pango/pango-glyph-item.c9
-rw-r--r--pango/pango-glyph-item.h11
-rw-r--r--pango/pango-layout-private.h5
-rw-r--r--pango/pango-layout.c201
-rw-r--r--pango/pango-markup.c58
-rw-r--r--pango/pango-renderer.c29
-rw-r--r--tests/markups/valid-4.expected8
-rw-r--r--tests/test-itemize.c2
-rw-r--r--tests/test-layout.c190
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;