[pango/break-tailoring: 25/28] Add a line-break attribute
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pango/break-tailoring: 25/28] Add a line-break attribute
- Date: Sun, 22 Aug 2021 06:54:54 +0000 (UTC)
commit edfdbdbf4dec01aa409708399a6b27d495bcacc4
Author: Matthias Clasen <mclasen redhat com>
Date: Sat Aug 21 11:47:33 2021 -0400
Add a line-break attribute
Add a line-break attribute that can be used to override the
line break classification at the beginning and end of the
attribute range.
Tests included.
docs/pango_markup.md | 6 ++++
pango/break.c | 56 ++++++++++++++++++++++++++++++++++++
pango/pango-attributes.c | 42 +++++++++++++++++++++++++++
pango/pango-attributes.h | 30 ++++++++++++++++++--
pango/pango-layout.c | 1 +
pango/pango-markup.c | 21 ++++++++++++++
tests/breaks/twelve.break | 2 ++
tests/breaks/twelve.expected | 6 ++++
tests/test-common.c | 5 ++++
tests/testattributes.c | 67 ++++++++++++++++++++++++++++++++++++++++++--
10 files changed, 232 insertions(+), 4 deletions(-)
---
diff --git a/docs/pango_markup.md b/docs/pango_markup.md
index 3a1cc311..d35c2a18 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.
+break_before
+break_after
+: The value can be one of 'none','char', 'line', or 'mandatory', to override
+ the line break classification at the beginning or end of the span. Available
+ since Pango 1.50.
+
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
diff --git a/pango/break.c b/pango/break.c
index 02a51a83..0bab3abd 100644
--- a/pango/break.c
+++ b/pango/break.c
@@ -1633,10 +1633,12 @@ break_attrs (const char *text,
int log_attrs_len)
{
PangoAttrList allow_breaks;
+ PangoAttrList line_breaks;
GSList *l;
gboolean tailored = FALSE;
_pango_attr_list_init (&allow_breaks);
+ _pango_attr_list_init (&line_breaks);
for (l = attributes; l; l = l->next)
{
@@ -1644,6 +1646,8 @@ break_attrs (const char *text,
if (attr->klass->type == PANGO_ATTR_ALLOW_BREAKS)
pango_attr_list_insert (&allow_breaks, pango_attribute_copy (attr));
+ else if (attr->klass->type == PANGO_ATTR_LINE_BREAK)
+ pango_attr_list_insert (&line_breaks, pango_attribute_copy (attr));
}
if (_pango_attr_list_has_attributes (&allow_breaks))
@@ -1687,7 +1691,54 @@ break_attrs (const char *text,
_pango_attr_iterator_destroy (&iter);
}
+ if (_pango_attr_list_has_attributes (&line_breaks))
+ {
+ PangoAttrIterator iter;
+
+ _pango_attr_list_get_iterator (&line_breaks, &iter);
+ do
+ {
+ const PangoAttribute *attr = pango_attr_iterator_get (&iter, PANGO_ATTR_LINE_BREAK);
+ PangoLineBreak before;
+ PangoLineBreak after;
+
+ if (!attr)
+ continue;
+
+ before = ((PangoAttrInt*)attr)->value & 0xfff;
+ after = ((PangoAttrInt*)attr)->value >> 16;
+
+ if (attr->start_index >= offset && before != 0)
+ {
+ int pos;
+
+ pos = g_utf8_pointer_to_offset (text, text + attr->start_index - offset);
+
+ log_attrs[pos].is_char_break = TRUE;
+ log_attrs[pos].is_line_break = before >= PANGO_LINE_BREAK_LINE;
+ log_attrs[pos].is_mandatory_break = before == PANGO_LINE_BREAK_MANDATORY;
+ tailored = TRUE;
+ }
+
+ if (attr->end_index < offset + length && after != 0)
+ {
+ int pos;
+
+ pos = g_utf8_pointer_to_offset (text, text + attr->end_index - offset);
+
+ log_attrs[pos].is_char_break = TRUE;
+ log_attrs[pos].is_line_break = after >= PANGO_LINE_BREAK_LINE;
+ log_attrs[pos].is_mandatory_break = after == PANGO_LINE_BREAK_MANDATORY;
+ tailored = TRUE;
+ }
+ }
+ while (pango_attr_iterator_next (&iter));
+
+ _pango_attr_iterator_destroy (&iter);
+ }
+
_pango_attr_list_destroy (&allow_breaks);
+ _pango_attr_list_destroy (&line_breaks);
return tailored;
}
@@ -2213,6 +2264,11 @@ pango_tailor_break (const char *text,
* The line breaks are assumed to have been produced
* by [func@Pango.default_break] and [func@Pango.tailor_break].
*
+ * Note that `PANGO_ATTR_ALLOW_BREAKS` attributes are applied
+ * before `PANGO_ATTR_LINE_BREAK` attributes, so it is possible
+ * to remove automatically determined breaks opportunities and
+ * then selectively introduce new ones.
+ *
* Since: 1.50
*/
void
diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c
index 28dc4105..501203f3 100644
--- a/pango/pango-attributes.c
+++ b/pango/pango-attributes.c
@@ -1302,6 +1302,47 @@ pango_attr_show_new (PangoShowFlags flags)
return pango_attr_int_new (&klass, (int)flags);
}
+/**
+ * pango_attr_line_break_new:
+ * @before: override for the beginning of the range
+ * @after: override for the end of the range
+ *
+ * Overrides the line break classification at the beginning
+ * and end of the range.
+ *
+ * The two values determine what line break opportunities
+ * to provide at the beginning and end of the attributes range.
+ *
+ * `PANGO_LINE_BREAK_IGNORE`
+ * : Don't change the line break classification.
+ * `PANGO_LINE_BREAK_NONE`
+ * : No line break opportunity.
+ * `PANGO_LINE_BEAK_CHAR`
+ * : A line break opportunities during character breaking.
+ * `PANGO_LINE_BREAK_LINE`
+ * : A regular line break opportunity.
+ * `PANGO_LINE_BREAK_MANDATORY:
+ * : A mandatory line break.
+ *
+ * Return value: (transfer full): the newly allocated
+ * `PangoAttribute`, which should be freed with
+ * [method@Pango.Attribute.destroy]
+ *
+ * Since: 1.50
+ */
+PangoAttribute *
+pango_attr_line_break_new (PangoLineBreak before,
+ PangoLineBreak after)
+{
+ static const PangoAttrClass klass = {
+ PANGO_ATTR_LINE_BREAK,
+ pango_attr_int_copy,
+ pango_attr_int_destroy,
+ pango_attr_int_equal,
+ };
+
+ return pango_attr_int_new (&klass, before | (after << 16));
+}
/**
* pango_attr_overline_new:
* @overline: the overline style
@@ -1472,6 +1513,7 @@ pango_attribute_as_int (PangoAttribute *attr)
case PANGO_ATTR_FOREGROUND_ALPHA:
case PANGO_ATTR_BACKGROUND_ALPHA:
case PANGO_ATTR_ALLOW_BREAKS:
+ case PANGO_ATTR_LINE_BREAK:
case PANGO_ATTR_SHOW:
case PANGO_ATTR_INSERT_HYPHENS:
case PANGO_ATTR_OVERLINE:
diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h
index 86826b62..ae193448 100644
--- a/pango/pango-attributes.h
+++ b/pango/pango-attributes.h
@@ -121,6 +121,7 @@ typedef enum
PANGO_ATTR_LINE_HEIGHT, /* PangoAttrFloat */
PANGO_ATTR_ABSOLUTE_LINE_HEIGHT, /* PangoAttrInt */
PANGO_ATTR_TEXT_TRANSFORM, /* PangoAttrInt */
+ PANGO_ATTR_LINE_BREAK, /* PangoAttrInt */
} PangoAttrType;
/**
@@ -222,6 +223,28 @@ typedef enum {
PANGO_TEXT_TRANSFORM_CAPITALIZE,
} PangoTextTransform;
+/**
+ * PangoLineBreak:
+ * @PANGO_LINE_BREAK_IGNORE: An ignored value
+ * @PANGO_LINE_BREAK_NONE: No line break opportunity
+ * @PANGO_LINE_BREAK_CHAR: Line break opportunity when doing character wrapping
+ * @PANGO_LINE_BREAK_LINE: Line break opportunity
+ * @PANGO_LINE_BREAK_MANDATORY: Mandatory break
+ *
+ * These values are used in attributes that modify line break classification.
+ *
+ * The `PANGO_ATTR_LINE_BREAK` attribute combines two `PangoLineBreak`
+ * values into its value, one for the start of the range, and another
+ * for the end of the range, shifted by 16.
+ */
+typedef enum {
+ PANGO_LINE_BREAK_IGNORE,
+ PANGO_LINE_BREAK_NONE,
+ PANGO_LINE_BREAK_CHAR,
+ PANGO_LINE_BREAK_LINE,
+ PANGO_LINE_BREAK_MANDATORY,
+} PangoLineBreak;
+
/**
* PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING:
*
@@ -234,8 +257,7 @@ typedef enum {
/**
* PANGO_ATTR_INDEX_TO_TEXT_END: (value 4294967295)
- *
- * Value for @end_index in `PangoAttribute` that indicates
+ * * Value for @end_index in `PangoAttribute` that indicates
* the end of the text.
*
* Since: 1.24
@@ -538,6 +560,10 @@ PANGO_AVAILABLE_IN_1_38
PangoAttribute * pango_attr_background_alpha_new (guint16 alpha);
PANGO_AVAILABLE_IN_1_44
PangoAttribute * pango_attr_allow_breaks_new (gboolean allow_breaks);
+PANGO_AVAILABLE_IN_1_50
+PangoAttribute * pango_attr_line_break_new (PangoLineBreak before,
+ PangoLineBreak after);
+
PANGO_AVAILABLE_IN_1_44
PangoAttribute * pango_attr_insert_hyphens_new (gboolean
insert_hyphens);
PANGO_AVAILABLE_IN_1_46
diff --git a/pango/pango-layout.c b/pango/pango-layout.c
index 9cbd86f2..e832445c 100644
--- a/pango/pango-layout.c
+++ b/pango/pango-layout.c
@@ -4369,6 +4369,7 @@ affects_break_or_shape (PangoAttribute *attr,
{
/* Affects breaks */
case PANGO_ATTR_ALLOW_BREAKS:
+ case PANGO_ATTR_LINE_BREAK:
/* Affects shaping */
case PANGO_ATTR_INSERT_HYPHENS:
case PANGO_ATTR_FONT_FEATURES:
diff --git a/pango/pango-markup.c b/pango/pango-markup.c
index 22064103..f9c143c6 100644
--- a/pango/pango-markup.c
+++ b/pango/pango-markup.c
@@ -1230,6 +1230,8 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED,
const char *show = NULL;
const char *line_height = NULL;
const char *text_transform = NULL;
+ const char *break_before = NULL;
+ const char *break_after = NULL;
g_markup_parse_context_get_position (context,
&line_number, &char_number);
@@ -1267,6 +1269,8 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED,
CHECK_ATTRIBUTE2(background, "bgcolor");
CHECK_ATTRIBUTE (background_alpha);
CHECK_ATTRIBUTE2(background_alpha, "bgalpha");
+ CHECK_ATTRIBUTE (break_before);
+ CHECK_ATTRIBUTE (break_after);
break;
case 'c':
CHECK_ATTRIBUTE2(foreground, "color");
@@ -1717,6 +1721,23 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED,
add_attribute (tag, pango_attr_allow_breaks_new (b));
}
+ if (G_UNLIKELY (break_before || break_after))
+ {
+ PangoLineBreak before, after;
+
+ if (!break_before)
+ before = 0;
+ else if (!span_parse_enum ("break_before", break_before, PANGO_TYPE_LINE_BREAK, (int*)(void*)&before,
line_number, error))
+ goto error;
+
+ if (!break_after)
+ after = 0;
+ else if (!span_parse_enum ("break_after", break_after, PANGO_TYPE_LINE_BREAK, (int*)(void*)&after,
line_number, error))
+ goto error;
+
+ add_attribute (tag, pango_attr_line_break_new (before, after));
+ }
+
if (G_UNLIKELY (insert_hyphens))
{
gboolean b = FALSE;
diff --git a/tests/breaks/twelve.break b/tests/breaks/twelve.break
new file mode 100644
index 00000000..cf3c8aa1
--- /dev/null
+++ b/tests/breaks/twelve.break
@@ -0,0 +1,2 @@
+# test line break attributes
+the file <span allow_breaks='false'><span break_before='mandatory' break_after='line'>/path/</span><span
break_after='line'>to/</span><span break_after='line'>my/</span>home</span> is cursed.
diff --git a/tests/breaks/twelve.expected b/tests/breaks/twelve.expected
new file mode 100644
index 00000000..3d9bd4ae
--- /dev/null
+++ b/tests/breaks/twelve.expected
@@ -0,0 +1,6 @@
+Text: t h e [ ] f i l e [ ] / p a t h / t o / m y /
h o m e [ ] i s [ ] c u r s e d . [0x0a]
+Breaks: c c c c lc c c c c Lc lc lc lc c lc c c lc c c c c c c
c c
+Whitespace: x x x x
w w
+Sentences: bs
e b
+Words: bs be bs be b bs be bs be bs be bs be bs be bs be
b b
+Graphemes: b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b
b b
diff --git a/tests/test-common.c b/tests/test-common.c
index 011b2eef..12ea1cb0 100644
--- a/tests/test-common.c
+++ b/tests/test-common.c
@@ -146,6 +146,11 @@ print_attribute (PangoAttribute *attr, GString *string)
case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
g_string_append_printf (string, "%d", ((PangoAttrInt *)attr)->value);
break;
+ case PANGO_ATTR_LINE_BREAK:
+ g_string_append_printf (string, "%d, %d",
+ ((PangoAttrInt *)attr)->value & 0xffff,
+ ((PangoAttrInt *)attr)->value >> 16);
+ break;
case PANGO_ATTR_FONT_DESC:
{
char *text = pango_font_description_to_string (((PangoAttrFontDesc *)attr)->desc);
diff --git a/tests/testattributes.c b/tests/testattributes.c
index f950a204..531fa1cc 100644
--- a/tests/testattributes.c
+++ b/tests/testattributes.c
@@ -75,6 +75,7 @@ test_attributes_basic (void)
test_copy (pango_attr_text_transform_new (PANGO_TEXT_TRANSFORM_UPPERCASE));
test_copy (pango_attr_line_height_new (1.5));
test_copy (pango_attr_line_height_new_absolute (3000));
+ test_copy (pango_attr_line_break_new (PANGO_LINE_BREAK_CHAR, PANGO_LINE_BREAK_MANDATORY));
}
static void
@@ -207,6 +208,7 @@ test_binding_helpers (void)
test_binding (pango_attr_text_transform_new (PANGO_TEXT_TRANSFORM_UPPERCASE));
test_binding (pango_attr_line_height_new (1.5));
test_binding (pango_attr_line_height_new_absolute (3000));
+ test_binding (pango_attr_line_break_new (PANGO_LINE_BREAK_CHAR, PANGO_LINE_BREAK_MANDATORY));
}
static void
@@ -1213,8 +1215,8 @@ test_merge2 (void)
pango_attr_list_unref (list);
}
-/* This only prints rise, size and scale, which are the
- * only relevant attributes in the test that uses this
+/* This only prints rise, size, scale, allow_breaks and line_break,
+ * which are the only relevant attributes in the tests that use this
* function.
*/
static void
@@ -1240,6 +1242,19 @@ print_tags_for_attributes (PangoAttrIterator *iter,
g_string_append_printf (s, "[%d, %d]scale=%f\n",
attr->start_index, attr->end_index,
((PangoAttrFloat*)attr)->value);
+
+ attr = pango_attr_iterator_get (iter, PANGO_ATTR_ALLOW_BREAKS);
+ if (attr)
+ g_string_append_printf (s, "[%d, %d]allow_breaks=%d\n",
+ attr->start_index, attr->end_index,
+ ((PangoAttrInt*)attr)->value);
+
+ attr = pango_attr_iterator_get (iter, PANGO_ATTR_LINE_BREAK);
+ if (attr)
+ g_string_append_printf (s, "[%d, %d]before=%d,after=%d\n",
+ attr->start_index, attr->end_index,
+ ((PangoAttrInt*)attr)->value & 0xffff,
+ ((PangoAttrInt*)attr)->value >> 16);
}
static void
@@ -1301,6 +1316,53 @@ test_iter_epsilon_zero (void)
g_string_free (s, TRUE);
}
+static void
+test_iter_line_breaks (void)
+{
+ const char *markup = "<span allow_breaks='false'>a<span break_before='line'>b</span><span
break_after='mandatory'>c</span></span>";
+ PangoAttrList *attributes;
+ PangoAttrIterator *attr;
+ char *text;
+ GError *error = NULL;
+ GString *s;
+
+ s = g_string_new ("");
+
+ pango_parse_markup (markup, -1, 0, &attributes, &text, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (text, ==, "abc");
+
+ attr = pango_attr_list_get_iterator (attributes);
+ do
+ {
+ int start, end;
+
+ pango_attr_iterator_range (attr, &start, &end);
+
+ g_string_append_printf (s, "range: [%d, %d]\n", start, end);
+
+ print_tags_for_attributes (attr, s);
+ }
+ while (pango_attr_iterator_next (attr));
+
+ g_free (text);
+ pango_attr_list_unref (attributes);
+ pango_attr_iterator_destroy (attr);
+
+ g_assert_cmpstr (s->str, ==,
+ "range: [0, 1]\n"
+ "[0, 3]allow_breaks=0\n"
+ "range: [1, 2]\n"
+ "[0, 3]allow_breaks=0\n"
+ "[1, 2]before=3,after=0\n"
+ "range: [2, 3]\n"
+ "[0, 3]allow_breaks=0\n"
+ "[2, 3]before=0,after=4\n"
+ "range: [3, 2147483647]\n");
+
+ g_string_free (s, TRUE);
+}
+
int
main (int argc, char *argv[])
{
@@ -1340,6 +1402,7 @@ main (int argc, char *argv[])
g_test_add_func ("/attributes/iter/get_font", test_iter_get_font);
g_test_add_func ("/attributes/iter/get_attrs", test_iter_get_attrs);
g_test_add_func ("/attributes/iter/epsilon_zero", test_iter_epsilon_zero);
+ g_test_add_func ("/attributes/iter/line_breaks", test_iter_line_breaks);
return g_test_run ();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]