diff options
author | Matthias Clasen <mclasen@redhat.com> | 2019-07-22 16:20:37 +0000 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2019-07-22 16:20:37 +0000 |
commit | f401cb1789400384aca0e3679e47213148c9403b (patch) | |
tree | b2c949dda55a6199efdd35d0815acf384af8b065 /pango | |
parent | 5319f448a052ab328deefea2a6a697fab2903ddf (diff) | |
parent | 2036d2b6be2254f97f23944b04a58d224bd1409d (diff) | |
download | pango-f401cb1789400384aca0e3679e47213148c9403b.tar.gz |
Merge branch 'better-hyphens' into 'master'
Better hyphens
See merge request GNOME/pango!89
Diffstat (limited to 'pango')
-rw-r--r-- | pango/break.c | 98 | ||||
-rw-r--r-- | pango/pango-attributes.c | 27 | ||||
-rw-r--r-- | pango/pango-attributes.h | 6 | ||||
-rw-r--r-- | pango/pango-break.h | 5 | ||||
-rw-r--r-- | pango/pango-item.h | 12 | ||||
-rw-r--r-- | pango/pango-layout.c | 207 | ||||
-rw-r--r-- | pango/pango-markup.c | 17 | ||||
-rw-r--r-- | pango/pangofc-font.c | 4 | ||||
-rw-r--r-- | pango/pangofc-shape.c | 14 |
9 files changed, 240 insertions, 150 deletions
diff --git a/pango/break.c b/pango/break.c index 8ed8e8c1..356c5afe 100644 --- a/pango/break.c +++ b/pango/break.c @@ -1578,18 +1578,34 @@ break_script (const char *item_text, int attrs_len); static gboolean -tailor_break (const gchar *text, - gint length, - PangoAnalysis *analysis, - PangoLogAttr *attrs, - int attrs_len) +break_attrs (const char *text, + int length, + GSList *attributes, + int item_offset, + PangoLogAttr *attrs, + int attrs_len); + +static gboolean +tailor_break (const char *text, + int length, + PangoAnalysis *analysis, + int item_offset, + PangoLogAttr *attrs, + int attrs_len) { + gboolean res; + if (length < 0) length = strlen (text); else if (text == NULL) text = ""; - return break_script (text, length, analysis, attrs, attrs_len); + res = break_script (text, length, analysis, attrs, attrs_len); + + if (item_offset >= 0) + res |= break_attrs (text, length, analysis->extra_attrs, item_offset, attrs, attrs_len); + + return res; } /** @@ -1618,7 +1634,7 @@ pango_break (const gchar *text, g_return_if_fail (attrs != NULL); pango_default_break (text, length, analysis, attrs, attrs_len); - tailor_break (text, length, analysis, attrs, attrs_len); + tailor_break (text, length, analysis, -1, attrs, attrs_len); } /** @@ -1726,6 +1742,8 @@ pango_find_paragraph_boundary (const gchar *text, * @text: text to process. Must be valid UTF-8 * @length: length in bytes of @text * @analysis: #PangoAnalysis structure from pango_itemize() for @text + * @offset: Byte offset of @text from the beginning of the + * paragraph, or -1 to ignore attributes from @analysis * @log_attrs: (array length=attrs_len): array with one #PangoLogAttr * per character in @text, plus one extra, to be filled in * @attrs_len: length of @log_attrs array @@ -1733,18 +1751,24 @@ pango_find_paragraph_boundary (const gchar *text, * Apply language-specific tailoring to the breaks in * @log_attrs, which are assumed to have been produced * by pango_default_break(). + * + * If @offset is not -1, it is used to apply attributes + * from @analysis that are relevant to line breaking. + * + * Since: 1.44 */ void pango_tailor_break (const char *text, int length, PangoAnalysis *analysis, + int offset, PangoLogAttr *log_attrs, int log_attrs_len) { PangoLogAttr *start = log_attrs; PangoLogAttr attr_before = *start; - if (tailor_break (text, length, analysis, log_attrs, log_attrs_len)) + if (tailor_break (text, length, analysis, offset, log_attrs, log_attrs_len)) { /* if tailored, we enforce some of the attrs from before * tailoring at the boundary @@ -1773,6 +1797,7 @@ tailor_segment (const char *range_start, pango_tailor_break (range_start, range_end - range_start, analysis, + -1, start, chars_in_range + 1); @@ -1877,3 +1902,60 @@ break_script (const char *item_text, return TRUE; } + +static gboolean +break_attrs (const char *text, + int length, + GSList *attributes, + int offset, + PangoLogAttr *log_attrs, + int log_attrs_len) +{ + PangoAttrList *list; + PangoAttrIterator *iter; + GSList *l; + + list = pango_attr_list_new (); + for (l = attributes; l; l = l->next) + { + PangoAttribute *attr = l->data; + + if (attr->klass->type == PANGO_ATTR_ALLOW_BREAKS) + pango_attr_list_insert (list, (PangoAttribute*)l->data); + } + + iter = pango_attr_list_get_iterator (list); + do { + PangoAttribute *attr; + + attr = pango_attr_iterator_get (iter, PANGO_ATTR_ALLOW_BREAKS); + if (attr && ((PangoAttrInt*)attr)->value == 0) + { + int start, end; + int start_pos, end_pos; + int pos; + + pango_attr_iterator_range (iter, &start, &end); + if (start < offset) + start_pos = 0; + else + start_pos = g_utf8_pointer_to_offset (text, text + start - offset); + if (end >= offset + length) + end_pos = log_attrs_len; + else + end_pos = g_utf8_pointer_to_offset (text, text + end - offset); + + for (pos = start_pos + 1; pos < end_pos; pos++) + { + log_attrs[pos].is_mandatory_break = FALSE; + log_attrs[pos].is_line_break = FALSE; + log_attrs[pos].is_char_break = FALSE; + } + } + } while (pango_attr_iterator_next (iter)); + + pango_attr_iterator_destroy (iter); + pango_attr_list_unref (list); + + return TRUE; +} diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c index 894555d1..15edfbe8 100644 --- a/pango/pango-attributes.c +++ b/pango/pango-attributes.c @@ -1179,6 +1179,33 @@ pango_attr_background_alpha_new (guint16 alpha) return pango_attr_int_new (&klass, (int)alpha); } +/** + * pango_attr_allow_breaks_new: + * @allow_breaks: %TRUE if we line breaks are allowed + * + * Create a new allow-breaks attribute. + * + * If breaks are disabled, the range will be kept in a + * single run, as far as possible. + * + * Return value: (transfer full): the newly allocated #PangoAttribute, + * which should be freed with pango_attribute_destroy() + * + * Since: 1.44 + */ +PangoAttribute * +pango_attr_allow_breaks_new (gboolean allow_breaks) +{ + static const PangoAttrClass klass = { + PANGO_ATTR_ALLOW_BREAKS, + pango_attr_int_copy, + pango_attr_int_destroy, + pango_attr_int_equal, + }; + + return pango_attr_int_new (&klass, (int)allow_breaks); +} + /* * Attribute List */ diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h index 6f021c06..f7a0c1cb 100644 --- a/pango/pango-attributes.h +++ b/pango/pango-attributes.h @@ -147,6 +147,7 @@ typedef struct _PangoAttrIterator PangoAttrIterator; * @PANGO_ATTR_FONT_FEATURES: OpenType font features (#PangoAttrString). Since 1.38 * @PANGO_ATTR_FOREGROUND_ALPHA: foreground alpha (#PangoAttrInt). Since 1.38 * @PANGO_ATTR_BACKGROUND_ALPHA: background alpha (#PangoAttrInt). Since 1.38 + * @PANGO_ATTR_ALLOW_BREAKS: whether breaks are allowed (#PangoAttrInt). Since 1.44 * * The #PangoAttrType * distinguishes between different types of attributes. Along with the @@ -182,7 +183,8 @@ typedef enum PANGO_ATTR_GRAVITY_HINT, /* PangoAttrInt */ PANGO_ATTR_FONT_FEATURES, /* PangoAttrString */ PANGO_ATTR_FOREGROUND_ALPHA, /* PangoAttrInt */ - PANGO_ATTR_BACKGROUND_ALPHA /* PangoAttrInt */ + PANGO_ATTR_BACKGROUND_ALPHA, /* PangoAttrInt */ + PANGO_ATTR_ALLOW_BREAKS /* PangoAttrInt */ } PangoAttrType; /** @@ -522,6 +524,8 @@ PANGO_AVAILABLE_IN_1_38 PangoAttribute *pango_attr_foreground_alpha_new (guint16 alpha); 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_ALL GType pango_attr_list_get_type (void) G_GNUC_CONST; diff --git a/pango/pango-break.h b/pango/pango-break.h index 4e1db0bc..19698db1 100644 --- a/pango/pango-break.h +++ b/pango/pango-break.h @@ -134,9 +134,10 @@ void pango_default_break (const gchar *text, int attrs_len); PANGO_AVAILABLE_IN_1_44 -void pango_tailor_break (const gchar *text, - int length, +void pango_tailor_break (const char *text, + int length, PangoAnalysis *analysis, + int offset, PangoLogAttr *attrs, int attrs_len); diff --git a/pango/pango-item.h b/pango/pango-item.h index 2a7bcb2a..d4162f90 100644 --- a/pango/pango-item.h +++ b/pango/pango-item.h @@ -51,13 +51,23 @@ typedef struct _PangoItem PangoItem; #define PANGO_ANALYSIS_FLAG_IS_ELLIPSIS (1 << 1) /** + * PANGO_ANALYSIS_FLAG_NEED_HYPHEN: + * + * This flag tells Pango to add a hyphen at the end of the + * run during shaping. + * + * Since: 1.44 + */ +#define PANGO_ANALYSIS_FLAG_NEED_HYPHEN (1 << 2) + +/** * PangoAnalysis: * @shape_engine: unused * @lang_engine: unused * @font: the font for this segment. * @level: the bidirectional level for this segment. * @gravity: the glyph orientation for this segment (A #PangoGravity). - * @flags: boolean flags for this segment (currently only one) (Since: 1.16). + * @flags: boolean flags for this segment (Since: 1.16). * @script: the detected script for this segment (A #PangoScript) (Since: 1.18). * @language: the detected language for this segment. * @extra_attrs: extra attributes for this segment. diff --git a/pango/pango-layout.c b/pango/pango-layout.c index 7735d6f2..414525d7 100644 --- a/pango/pango-layout.c +++ b/pango/pango-layout.c @@ -3337,9 +3337,9 @@ shape_run (PangoLayoutLine *line, state->properties.shape_ink_rect, state->properties.shape_logical_rect, glyphs); else - pango_shape_full (layout->text + item->offset, item->length, - layout->text, layout->length, - &item->analysis, glyphs); + pango_shape_full (layout->text + item->offset, item->length, + layout->text, layout->length, + &item->analysis, glyphs); if (state->properties.letter_spacing) { @@ -3396,73 +3396,6 @@ insert_run (PangoLayoutLine *line, } static void -advance_iterator_to (PangoAttrIterator *iter, - int new_index) -{ - int start, end; - - do - { - pango_attr_iterator_range (iter, &start, &end); - if (end > new_index) - break; - } - while (pango_attr_iterator_next (iter)); -} - -static PangoLayoutRun * -create_hyphen_run (PangoLayout *layout, - PangoItem *item) -{ - PangoLayoutRun *run; - GList *items; - const char *hyphen_text = "-"; - int hyphen_len = 1; - PangoAttrList *attrs; - PangoAttrIterator *iter; - GSList *list, *l; - PangoAttrList *run_attrs; - - run_attrs = pango_attr_list_new (); - - attrs = pango_layout_get_effective_attributes (layout); - iter = pango_attr_list_get_iterator (attrs); - - advance_iterator_to (iter, item->offset + item->length); - list = pango_attr_iterator_get_attrs (iter); - for (l = list; l; l = l->next) - { - PangoAttribute *attr = l->data; - attr->start_index = 0; - attr->end_index = G_MAXINT; - pango_attr_list_insert (attrs, attr); - } - g_slist_free (list); - - items = pango_itemize (layout->context, hyphen_text, 0, hyphen_len, attrs, NULL); - g_assert (items->next == NULL); - - run = g_slice_new (PangoGlyphItem); - run->glyphs = pango_glyph_string_new (); - run->item = items->data; - - /* insert after item, use SHY as text */ - run->item->offset = item->offset + item->length; - run->item->length = 2; - run->item->num_chars = 1; - - g_list_free (items); - - pango_shape (hyphen_text, hyphen_len, &run->item->analysis, run->glyphs); - - pango_attr_iterator_destroy (iter); - pango_attr_list_unref (attrs); - pango_attr_list_unref (run_attrs); - - return run; -} - -static void get_need_hyphen (PangoItem *item, const char *text, int *need_hyphen) @@ -3472,8 +3405,37 @@ get_need_hyphen (PangoItem *item, for (i = 0, p = text + item->offset; i < item->num_chars; i++, p = g_utf8_next_char (p)) { - gunichar ch = g_utf8_get_char (p); - need_hyphen[i] = ch == 0xad; + gunichar wc = g_utf8_get_char (p); + switch (g_unichar_type(wc)) + { + case G_UNICODE_SPACE_SEPARATOR: + case G_UNICODE_LINE_SEPARATOR: + case G_UNICODE_PARAGRAPH_SEPARATOR: + need_hyphen[i] = FALSE; + break; + case G_UNICODE_CONTROL: + if (wc == '\t' || wc == '\n' || wc == '\r' || wc == '\f') + need_hyphen[i] = FALSE; + else + need_hyphen[i] = TRUE; + break; + default: + if (wc == '-' || /* Hyphen-minus */ + wc == 0x058a || /* Armenian hyphen */ + wc == 0x1400 || /* Canadian syllabics hyphen */ + wc == 0x1806 || /* Mongolian todo hyphen */ + wc == 0x2010 || /* Hyphen */ + wc == 0x2027 || /* Hyphenation point */ + wc == 0x2e17 || /* Double oblique hyphen */ + wc == 0x2e40 || /* Double hyphen */ + wc == 0x30a0 || /* Katakana-Hiragana double hyphen */ + wc == 0xfe63 || /* Small hyphen-minus */ + wc == 0xff0d) /* Fullwidth hyphen-minus */ + need_hyphen[i] = FALSE; + else + need_hyphen[i] = TRUE; + break; + } } } @@ -3492,22 +3454,36 @@ break_needs_hyphen (PangoLayout *layout, } static int +find_hyphen_width (PangoItem *item) +{ + hb_font_t *hb_font; + hb_codepoint_t glyph; + + /* This is not technically correct, since + * a) we may end up inserting a different hyphen + * b) we should reshape the entire run + * But it is close enough in practice + */ + hb_font = pango_font_get_hb_font (item->analysis.font); + if (hb_font_get_nominal_glyph (hb_font, 0x2010, &glyph) || + hb_font_get_nominal_glyph (hb_font, '-', &glyph)) + return hb_font_get_glyph_h_advance (hb_font, glyph); + + return 0; +} + +static int find_break_extra_width (PangoLayout *layout, ParaBreakState *state, int pos) { - PangoItem *item = state->items->data; - /* Check whether to insert a hyphen */ if (break_needs_hyphen (layout, state, pos)) { if (state->hyphen_width < 0) { - PangoLayoutRun *run; - - run = create_hyphen_run (layout, item); - state->hyphen_width = pango_glyph_string_get_width (run->glyphs); - pango_glyph_item_free (run); + PangoItem *item = state->items->data; + state->hyphen_width = find_hyphen_width (item); } return state->hyphen_width; @@ -3516,23 +3492,6 @@ find_break_extra_width (PangoLayout *layout, return 0; } -static void -insert_hyphen_after (PangoLayoutLine *line, - ParaBreakState *state, - PangoItem *item) -{ - PangoLayout *layout = line->layout; - PangoLayoutRun *run; - - /* Use the SHY as text for the hyphen run */ - item->num_chars -= 1; - item->length -= 2; - run = create_hyphen_run (layout, item); - line->runs = g_slist_prepend (line->runs, run); - - state->remaining_width -= pango_glyph_string_get_width (run->glyphs); -} - #if 0 # define DEBUG debug void @@ -3713,10 +3672,9 @@ process_item (PangoLayout *layout, if (break_num_chars == item->num_chars) { - gboolean insert_hyphen = break_needs_hyphen (layout, state, break_num_chars); + if (break_needs_hyphen (layout, state, break_num_chars)) + item->analysis.flags |= PANGO_ANALYSIS_FLAG_NEED_HYPHEN; insert_run (line, state, item, TRUE); - if (insert_hyphen) - insert_hyphen_after (line, state, item); return BREAK_ALL_FIT; } @@ -3727,26 +3685,24 @@ process_item (PangoLayout *layout, else { PangoItem *new_item; - gboolean insert_hyphen = break_needs_hyphen (layout, state, break_num_chars); length = g_utf8_offset_to_pointer (layout->text + item->offset, break_num_chars) - (layout->text + item->offset); new_item = pango_item_split (item, length, break_num_chars); + if (break_needs_hyphen (layout, state, break_num_chars)) + new_item->analysis.flags |= PANGO_ANALYSIS_FLAG_NEED_HYPHEN; /* Add the width back, to the line, reshape, subtract the new width */ state->remaining_width += break_width; insert_run (line, state, new_item, FALSE); break_width = pango_glyph_string_get_width (((PangoGlyphItem *)(line->runs->data))->glyphs); state->remaining_width -= break_width; + state->log_widths_offset += break_num_chars; + /* Shaped items should never be broken */ g_assert (!shape_set); - if (insert_hyphen) - insert_hyphen_after (line, state, new_item); - - state->log_widths_offset += break_num_chars; - return BREAK_SOME_FIT; } } @@ -3994,44 +3950,23 @@ get_items_log_attrs (const char *text, int log_attrs_len) { int offset = 0; - int index = 0; - int num_bytes = 0; int num_chars = 0; - PangoAnalysis analysis = { NULL }; GList *l; - analysis.level = -1; - - pango_default_break (text, length, &analysis, log_attrs, log_attrs_len); + pango_default_break (text, length, NULL, log_attrs, log_attrs_len); for (l = items; l; l = l->next) { PangoItem *item = l->data; - if (l == items) - { - analysis = item->analysis; - index = item->offset; - offset = 0; - } + pango_tailor_break (text + item->offset, + item->length, + &item->analysis, + item->offset, + log_attrs + offset, + item->num_chars + 1); - if (can_break_together (&analysis, &item->analysis)) - { - num_bytes += item->length; - num_chars += item->num_chars; - } - else - { - pango_tailor_break (text + index, - num_bytes, - &analysis, - log_attrs + offset, - num_chars + 1); - - analysis = item->analysis; - index += num_bytes; - offset += num_chars; - } + offset += num_chars; } } @@ -4119,9 +4054,9 @@ no_break_filter_func (PangoAttribute *attribute, gpointer data G_GNUC_UNUSED) { static const PangoAttrType no_break_types[] = { - PANGO_ATTR_FONT_FEATURES + PANGO_ATTR_FONT_FEATURES, + PANGO_ATTR_ALLOW_BREAKS }; - int i; for (i = 0; i < (int)G_N_ELEMENTS (no_break_types); i++) diff --git a/pango/pango-markup.c b/pango/pango-markup.c index 2c509dac..2b871584 100644 --- a/pango/pango-markup.c +++ b/pango/pango-markup.c @@ -153,6 +153,11 @@ * Fallback is enabled by default. Most applications should not * disable fallback. * + * allow_breaks + * : 'true' or 'false' whether to allow line breaks or not. If + * not allowed, the range will be kept in a single run as far + * as possible. Breaks are allowed by default. + * * lang * : A language code, indicating the text language * @@ -1297,6 +1302,7 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, const char *font_features = NULL; const char *alpha = NULL; const char *background_alpha = NULL; + const char *allow_breaks = NULL; g_markup_parse_context_get_position (context, &line_number, &char_number); @@ -1326,6 +1332,7 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, switch (names[i][0]) { case 'a': + CHECK_ATTRIBUTE (allow_breaks); CHECK_ATTRIBUTE (alpha); break; case 'b': @@ -1707,6 +1714,16 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, add_attribute (tag, pango_attr_font_features_new (font_features)); } + if (G_UNLIKELY (allow_breaks)) + { + gboolean b = FALSE; + + if (!span_parse_boolean ("allow_breaks", allow_breaks, &b, line_number, error)) + goto error; + + add_attribute (tag, pango_attr_allow_breaks_new (b)); + } + return TRUE; error: diff --git a/pango/pangofc-font.c b/pango/pangofc-font.c index 17119436..0bb25ba5 100644 --- a/pango/pangofc-font.c +++ b/pango/pangofc-font.c @@ -861,7 +861,7 @@ extern gpointer get_gravity_class (void); static PangoGravity pango_fc_font_key_get_gravity (PangoFcFontKey *key) { - FcPattern *pattern; + const FcPattern *pattern; PangoGravity gravity = PANGO_GRAVITY_SOUTH; FcChar8 *s; @@ -878,7 +878,7 @@ pango_fc_font_key_get_gravity (PangoFcFontKey *key) static double get_font_size (PangoFcFontKey *key) { - FcPattern *pattern; + const FcPattern *pattern; double size; double dpi; diff --git a/pango/pangofc-shape.c b/pango/pangofc-shape.c index 910ff4d7..12ce7ddb 100644 --- a/pango/pangofc-shape.c +++ b/pango/pangofc-shape.c @@ -158,6 +158,20 @@ pango_hb_shape (PangoFont *font, hb_buffer_set_flags (hb_buffer, HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT); hb_buffer_add_utf8 (hb_buffer, paragraph_text, paragraph_length, item_offset, item_length); + if (analysis->flags & PANGO_ANALYSIS_FLAG_NEED_HYPHEN) + { + /* Insert either a Unicode or ASCII hyphen. We may + * want to look for script-specific hyphens here. + */ + const char *p = paragraph_text + item_offset + item_length; + int last_char_len = p - g_utf8_prev_char (p); + hb_codepoint_t glyph; + + if (hb_font_get_nominal_glyph (hb_font, 0x2010, &glyph)) + hb_buffer_add (hb_buffer, 0x2010, item_length - last_char_len); + else if (hb_font_get_nominal_glyph (hb_font, '-', &glyph)) + hb_buffer_add (hb_buffer, '-', item_length - last_char_len); + } pango_font_get_features (font, features, G_N_ELEMENTS (features), &num_features); apply_extra_attributes (analysis->extra_attrs, features, G_N_ELEMENTS (features), &num_features); |