summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2021-08-09 03:04:21 +0000
committerMatthias Clasen <mclasen@redhat.com>2021-08-09 03:04:21 +0000
commitc4d138179443b49d817830c78ddcd1459cf5505f (patch)
tree105703add2c026aeff6d297d244bc6d9e2c579d7
parentf437641bcc412d7e7ac2f706487bbbdeaeddc87e (diff)
parent336dca6530d9dc41b101018d7f4af4a27e70445d (diff)
downloadpango-c4d138179443b49d817830c78ddcd1459cf5505f.tar.gz
Merge branch 'line-height-attribute' into 'main'
Implement css-like line-height See merge request GNOME/pango!390
-rw-r--r--docs/pango_markup.md6
-rw-r--r--pango/pango-attributes.c53
-rw-r--r--pango/pango-attributes.h8
-rw-r--r--pango/pango-layout.c114
-rw-r--r--pango/pango-markup.c38
-rw-r--r--tests/test-bidi.c12
6 files changed, 214 insertions, 17 deletions
diff --git a/docs/pango_markup.md b/docs/pango_markup.md
index 16359777..03718907 100644
--- a/docs/pango_markup.md
+++ b/docs/pango_markup.md
@@ -190,6 +190,12 @@ allow_breaks
: 'true' or 'false' to indicate whether breaking lines is allowed. Available
since Pango 1.44.
+line_height
+: Overrides the line height. The value can be either a factor (< 1024) that is
+ used to scale up the logical extents of runs or an absolute value (in 1024th
+ of a point).
+ Available since Pango 1.50.
+
## Convenience Tags
`<b>`
diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c
index 1a4a9443..fdb37f56 100644
--- a/pango/pango-attributes.c
+++ b/pango/pango-attributes.c
@@ -1341,6 +1341,58 @@ pango_attr_overline_color_new (guint16 red,
return pango_attr_color_new (&klass, red, green, blue);
}
+/**
+ * pango_attr_line_height_new:
+ * @factor: the scaling factor to apply to the logical height
+ *
+ * Modify the height of logical line extents by a factor.
+ *
+ * This affects the values returned by
+ * [method@Pango.LayoutLine.get_extents],
+ * [method@Pango.LayoutLine.get_pixel_extents] and
+ * [method@Pango.LayoutIter.get_line_extents].
+ *
+ *
+ * Since: 1.50
+ */
+PangoAttribute *
+pango_attr_line_height_new (double factor)
+{
+ static const PangoAttrClass klass = {
+ PANGO_ATTR_LINE_HEIGHT,
+ pango_attr_float_copy,
+ pango_attr_float_destroy,
+ pango_attr_float_equal
+ };
+
+ return pango_attr_float_new (&klass, factor);
+}
+
+/**
+ * pango_attr_line_height_new_absolute:
+ * @height: the line height, in %PANGO_SCALE-ths of a point
+ *
+ * Override the height of logical line extents to be @height.
+ *
+ * This affects the values returned by
+ * [method@Pango.LayoutLine.get_extents],
+ * [method@Pango.LayoutLine.get_pixel_extents] and
+ * [method@Pango.LayoutIter.get_line_extents].
+ *
+ * Since: 1.50
+ */
+PangoAttribute *
+pango_attr_line_height_new_absolute (int height)
+{
+ static const PangoAttrClass klass = {
+ PANGO_ATTR_ABSOLUTE_LINE_HEIGHT,
+ pango_attr_int_copy,
+ pango_attr_int_destroy,
+ pango_attr_int_equal
+ };
+
+ return pango_attr_int_new (&klass, height);
+}
/*
* Attribute List
*/
@@ -2559,6 +2611,7 @@ pango_attribute_as_float (PangoAttribute *attr)
switch (attr->klass->type)
{
case PANGO_ATTR_SCALE:
+ case PANGO_ATTR_LINE_HEIGHT:
return (PangoAttrFloat *)attr;
default:
diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h
index 6f18718e..c28623fb 100644
--- a/pango/pango-attributes.h
+++ b/pango/pango-attributes.h
@@ -160,6 +160,7 @@ typedef struct _PangoAttrIterator PangoAttrIterator;
* @PANGO_ATTR_INSERT_HYPHENS: whether to insert hyphens at intra-word line breaks ([struct@Pango.AttrInt]). Since 1.44
* @PANGO_ATTR_OVERLINE: whether the text has an overline ([struct@Pango.AttrInt]). Since 1.46
* @PANGO_ATTR_OVERLINE_COLOR: overline color ([struct@Pango.AttrColor]). Since 1.46
+ * @PANGO_ATTR_LINE_HEIGHT: line height factor ([struct@Pango.AttrFloat]). Since: 1.50
*
* The `PangoAttrType` distinguishes between different types of attributes.
*
@@ -201,6 +202,8 @@ typedef enum
PANGO_ATTR_INSERT_HYPHENS, /* PangoAttrInt */
PANGO_ATTR_OVERLINE, /* PangoAttrInt */
PANGO_ATTR_OVERLINE_COLOR, /* PangoAttrColor */
+ PANGO_ATTR_LINE_HEIGHT, /* PangoAttrFloat */
+ PANGO_ATTR_ABSOLUTE_LINE_HEIGHT, /* PangoAttrInt */
} PangoAttrType;
/**
@@ -611,6 +614,11 @@ typedef enum {
PANGO_AVAILABLE_IN_1_44
PangoAttribute *pango_attr_show_new (PangoShowFlags flags);
+PANGO_AVAILABLE_IN_1_50
+PangoAttribute *pango_attr_line_height_new (double factor);
+PANGO_AVAILABLE_IN_1_50
+PangoAttribute *pango_attr_line_height_new_absolute (int height);
+
PANGO_AVAILABLE_IN_ALL
GType pango_attr_list_get_type (void) G_GNUC_CONST;
PANGO_AVAILABLE_IN_ALL
diff --git a/pango/pango-layout.c b/pango/pango-layout.c
index e0475a5e..2a3cb5a2 100644
--- a/pango/pango-layout.c
+++ b/pango/pango-layout.c
@@ -78,6 +78,7 @@
#include "pango-impl-utils.h"
#include "pango-glyph-item.h"
#include <string.h>
+#include <math.h>
#include "pango-layout-private.h"
#include "pango-attributes-private.h"
@@ -107,6 +108,8 @@ struct _ItemProperties
gboolean shape_set;
PangoRectangle *shape_ink_rect;
PangoRectangle *shape_logical_rect;
+ double line_height;
+ int absolute_line_height;
};
typedef struct _PangoLayoutLinePrivate PangoLayoutLinePrivate;
@@ -177,7 +180,8 @@ static void pango_layout_line_postprocess (PangoLayoutLine *line,
static void pango_layout_line_leaked (PangoLayoutLine *line);
/* doesn't leak line */
-static PangoLayoutLine* _pango_layout_iter_get_line (PangoLayoutIter *iter);
+static PangoLayoutLine * _pango_layout_iter_get_line (PangoLayoutIter *iter);
+static PangoLayoutRun * _pango_layout_iter_get_run (PangoLayoutIter *iter);
static void pango_layout_get_item_properties (PangoItem *item,
ItemProperties *properties);
@@ -587,9 +591,12 @@ pango_layout_get_indent (PangoLayout *layout)
* The default value is 0.
*
* Note: Since 1.44, Pango is using the line height (as determined
- * by the font) for placing lines when the line height factor is set
+ * by the font) for placing lines when the line spacing factor is set
* to a non-zero value with [method@Pango.Layout.set_line_spacing].
* In that case, the @spacing set with this function is ignored.
+ *
+ * Note: for semantics that are closer to the CSS line-height
+ * property, see [func@Pango.attr_line_height_new].
*/
void
pango_layout_set_spacing (PangoLayout *layout,
@@ -638,6 +645,9 @@ pango_layout_get_spacing (PangoLayout *layout)
*
* If @factor is zero (the default), spacing is applied as before.
*
+ * Note: for semantics that are closer to the CSS line-height
+ * property, see [func@Pango.attr_line_height_new].
+ *
* Since: 1.44
*/
void
@@ -1793,7 +1803,8 @@ pango_layout_index_to_line (PangoLayout *layout,
static PangoLayoutLine *
pango_layout_index_to_line_and_extents (PangoLayout *layout,
int index,
- PangoRectangle *line_rect)
+ PangoRectangle *line_rect,
+ PangoRectangle *run_rect)
{
PangoLayoutIter iter;
PangoLayoutLine *line = NULL;
@@ -1813,7 +1824,26 @@ pango_layout_index_to_line_and_extents (PangoLayout *layout,
pango_layout_iter_get_line_extents (&iter, NULL, line_rect);
if (line->start_index + line->length > index)
- break;
+ {
+ if (run_rect)
+ {
+ while (TRUE)
+ {
+ PangoLayoutRun *run = _pango_layout_iter_get_run (&iter);
+
+ if (run->item->offset <= index && index < run->item->offset + run->item->length)
+ {
+ pango_layout_iter_get_run_extents (&iter, NULL, run_rect);
+ break;
+ }
+
+ if (!pango_layout_iter_next_run (&iter))
+ break;
+ }
+ }
+
+ break;
+ }
if (!pango_layout_iter_next_line (&iter))
break; /* Use end of last line */
@@ -2291,10 +2321,24 @@ pango_layout_index_to_pos (PangoLayout *layout,
layout_line = tmp_line;
- pango_layout_iter_get_line_extents (&iter, NULL, &logical_rect);
-
if (layout_line->start_index + layout_line->length > index)
- break;
+ {
+ while (TRUE)
+ {
+ PangoLayoutRun *run = _pango_layout_iter_get_run (&iter);
+
+ if (run->item->offset <= index && index < run->item->offset + run->item->length)
+ {
+ pango_layout_iter_get_run_extents (&iter, NULL, &logical_rect);
+ break;
+ }
+
+ if (!pango_layout_iter_next_run (&iter))
+ break;
+ }
+
+ break;
+ }
if (!pango_layout_iter_next_line (&iter))
{
@@ -2367,7 +2411,7 @@ pango_layout_get_direction (PangoLayout *layout,
{
PangoLayoutLine *line;
- line = pango_layout_index_to_line_and_extents (layout, index, NULL);
+ line = pango_layout_index_to_line_and_extents (layout, index, NULL, NULL);
if (line)
return pango_layout_line_get_char_direction (line, index);
@@ -2400,6 +2444,7 @@ pango_layout_get_cursor_pos (PangoLayout *layout,
PangoDirection dir1, dir2;
int level1, level2;
PangoRectangle line_rect;
+ PangoRectangle run_rect;
PangoLayoutLine *layout_line = NULL; /* Quiet GCC */
int x1_trailing;
int x2;
@@ -2408,7 +2453,7 @@ pango_layout_get_cursor_pos (PangoLayout *layout,
g_return_if_fail (index >= 0 && index <= layout->length);
layout_line = pango_layout_index_to_line_and_extents (layout, index,
- &line_rect);
+ &line_rect, &run_rect);
g_assert (index >= layout_line->start_index);
@@ -2457,9 +2502,9 @@ pango_layout_get_cursor_pos (PangoLayout *layout,
else
strong_pos->x += x2;
- strong_pos->y = line_rect.y;
+ strong_pos->y = run_rect.y;
strong_pos->width = 0;
- strong_pos->height = line_rect.height;
+ strong_pos->height = run_rect.height;
}
if (weak_pos)
@@ -2472,9 +2517,9 @@ pango_layout_get_cursor_pos (PangoLayout *layout,
else
weak_pos->x += x1_trailing;
- weak_pos->y = line_rect.y;
+ weak_pos->y = run_rect.y;
weak_pos->width = 0;
- weak_pos->height = line_rect.height;
+ weak_pos->height = run_rect.height;
}
}
@@ -4149,6 +4194,8 @@ affects_itemization (PangoAttribute *attr,
case PANGO_ATTR_LETTER_SPACING:
case PANGO_ATTR_SHAPE:
case PANGO_ATTR_RISE:
+ case PANGO_ATTR_LINE_HEIGHT:
+ case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
return TRUE;
default:
return FALSE;
@@ -5011,6 +5058,7 @@ static void
pango_layout_run_get_extents_and_height (PangoLayoutRun *run,
PangoRectangle *run_ink,
PangoRectangle *run_logical,
+ PangoRectangle *line_logical,
int *height)
{
PangoRectangle logical;
@@ -5019,7 +5067,7 @@ pango_layout_run_get_extents_and_height (PangoLayoutRun *run,
gboolean has_underline;
gboolean has_overline;
- if (G_UNLIKELY (!run_ink && !run_logical && !height))
+ if (G_UNLIKELY (!run_ink && !run_logical && !line_logical && !height))
return;
pango_layout_get_item_properties (run->item, &properties);
@@ -5034,6 +5082,9 @@ pango_layout_run_get_extents_and_height (PangoLayoutRun *run,
if (!run_logical && (has_underline || has_overline || properties.strikethrough))
run_logical = &logical;
+ if (!run_logical && line_logical)
+ run_logical = &logical;
+
if (properties.shape_set)
_pango_shape_get_extents (run->item->num_chars,
properties.shape_ink_rect,
@@ -5104,6 +5155,7 @@ pango_layout_run_get_extents_and_height (PangoLayoutRun *run,
if (!metrics)
metrics = pango_font_get_metrics (run->item->analysis.font,
run->item->analysis.language);
+
*height = pango_font_metrics_get_height (metrics);
}
@@ -5127,6 +5179,21 @@ pango_layout_run_get_extents_and_height (PangoLayoutRun *run,
run_logical->y -= properties.rise;
}
+ if (line_logical)
+ {
+ *line_logical = *run_logical;
+
+ if (properties.absolute_line_height != 0 || properties.line_height != 0.0)
+ {
+ int line_height, leading;
+
+ line_height = MAX (properties.absolute_line_height, ceilf (properties.line_height * line_logical->height));
+ leading = line_height - line_logical->height;
+ line_logical->y -= leading / 2;
+ line_logical->height += leading;
+ }
+ }
+
if (metrics)
pango_font_metrics_unref (metrics);
}
@@ -5206,6 +5273,7 @@ pango_layout_line_get_extents_and_height (PangoLayoutLine *line,
pango_layout_run_get_extents_and_height (run,
ink_rect ? &run_ink : NULL,
+ NULL,
&run_logical,
height ? &run_height : NULL);
@@ -5985,6 +6053,8 @@ pango_layout_get_item_properties (PangoItem *item,
properties->shape_set = FALSE;
properties->shape_ink_rect = NULL;
properties->shape_logical_rect = NULL;
+ properties->line_height = 0.0;
+ properties->absolute_line_height = 0;
while (tmp_list)
{
@@ -6048,6 +6118,14 @@ pango_layout_get_item_properties (PangoItem *item,
properties->shape_ink_rect = &((PangoAttrShape *)attr)->ink_rect;
break;
+ case PANGO_ATTR_LINE_HEIGHT:
+ properties->line_height = ((PangoAttrFloat *)attr)->value;
+ break;
+
+ case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
+ properties->absolute_line_height = ((PangoAttrInt *)attr)->value;
+ break;
+
default:
break;
}
@@ -6446,6 +6524,12 @@ _pango_layout_iter_get_line (PangoLayoutIter *iter)
return iter->line;
}
+static PangoLayoutRun *
+_pango_layout_iter_get_run (PangoLayoutIter *iter)
+{
+ return iter->run;
+}
+
/**
* pango_layout_iter_get_line:
* @iter: a `PangoLayoutIter`
@@ -6907,7 +6991,7 @@ pango_layout_iter_get_run_extents (PangoLayoutIter *iter,
if (iter->run)
{
- pango_layout_run_get_extents_and_height (iter->run, ink_rect, logical_rect, NULL);
+ pango_layout_run_get_extents_and_height (iter->run, ink_rect, logical_rect, NULL, NULL);
if (ink_rect)
{
diff --git a/pango/pango-markup.c b/pango/pango-markup.c
index 5394c772..00b16943 100644
--- a/pango/pango-markup.c
+++ b/pango/pango-markup.c
@@ -977,6 +977,29 @@ span_parse_int (const char *attr_name,
}
static gboolean
+span_parse_float (const char *attr_name,
+ const char *attr_val,
+ double *val,
+ int line_number,
+ GError **error)
+{
+ *val = g_ascii_strtod (attr_val, NULL);
+ if (errno != 0)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ _("Value of '%s' attribute on <span> tag "
+ "on line %d could not be parsed; "
+ "should be a number, not '%s'"),
+ attr_name, line_number, attr_val);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
span_parse_boolean (const char *attr_name,
const char *attr_val,
gboolean *val,
@@ -1200,6 +1223,7 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED,
const char *allow_breaks = NULL;
const char *insert_hyphens = NULL;
const char *show = NULL;
+ const char *line_height = NULL;
g_markup_parse_context_get_position (context,
&line_number, &char_number);
@@ -1278,6 +1302,7 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED,
case 'l':
CHECK_ATTRIBUTE (lang);
CHECK_ATTRIBUTE (letter_spacing);
+ CHECK_ATTRIBUTE (line_height);
break;
case 'o':
CHECK_ATTRIBUTE (overline);
@@ -1639,6 +1664,19 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED,
add_attribute (tag, pango_attr_letter_spacing_new (n));
}
+ if (G_UNLIKELY (line_height))
+ {
+ double f = 0;
+
+ if (!span_parse_float ("line_height", line_height, &f, line_number, error))
+ goto error;
+
+ if (f > 1024.0 && strchr (line_height, ".") == 0)
+ add_attribute (tag, pango_attr_line_height_new_absolute ((int)f));
+ else
+ add_attribute (tag, pango_attr_line_height_new (f));
+ }
+
if (G_UNLIKELY (lang))
{
add_attribute (tag,
diff --git a/tests/test-bidi.c b/tests/test-bidi.c
index 35f2a39d..7a137518 100644
--- a/tests/test-bidi.c
+++ b/tests/test-bidi.c
@@ -352,6 +352,9 @@ test_move_cursor_para (void)
int index;
int trailing;
const char *text;
+ int line_no;
+ PangoLayoutLine *line;
+ PangoRectangle ext;
layout = pango_layout_new (context);
@@ -387,9 +390,14 @@ test_move_cursor_para (void)
if (index >= strlen (tests[i].text) - 1)
break;
+ pango_layout_index_to_line_x (layout, index, FALSE, &line_no, NULL);
+ line = pango_layout_get_line (layout, line_no);
+ pango_layout_line_get_extents (line, NULL, &ext);
+
pango_layout_get_cursor_pos (layout, index, &pos, NULL);
- g_assert_true (pos.y > old_pos.y ||
- (pos.y == old_pos.y && pos.x > old_pos.x));
+ // assert that we are either moving to the right
+ // or jumping to the next line
+ g_assert_true (pos.y > ext.y + ext.height || pos.x > old_pos.x);
}
}