diff options
author | Matthias Clasen <mclasen@redhat.com> | 2019-07-25 04:54:05 +0000 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2019-07-25 04:54:05 +0000 |
commit | 997c73409594ef8b434b6d1b3da6c75ae1fcf536 (patch) | |
tree | ace6d1c1dc75a37032d595076064b6ba3596a22c | |
parent | 201afb727c0ca2e428711d25f74d0f0e717da7de (diff) | |
parent | 49cd1c08f86ee29761190268071779959c36009b (diff) | |
download | pango-997c73409594ef8b434b6d1b3da6c75ae1fcf536.tar.gz |
Merge branch 'visible-things' into 'master'
Make things visible
See merge request GNOME/pango!69
-rw-r--r-- | docs/pango-sections.txt | 2 | ||||
-rw-r--r-- | pango/pango-attributes.c | 25 | ||||
-rw-r--r-- | pango/pango-attributes.h | 25 | ||||
-rw-r--r-- | pango/pango-impl-utils.h | 70 | ||||
-rw-r--r-- | pango/pango-layout.c | 37 | ||||
-rw-r--r-- | pango/pango-markup.c | 43 | ||||
-rw-r--r-- | pango/pango-utils-internal.h | 4 | ||||
-rw-r--r-- | pango/pango-utils.c | 62 | ||||
-rw-r--r-- | pango/pangocairo-font.c | 21 | ||||
-rw-r--r-- | pango/pangocairo-render.c | 103 | ||||
-rw-r--r-- | pango/pangofc-shape.c | 248 | ||||
-rw-r--r-- | tests/test-common.c | 1 | ||||
-rw-r--r-- | tests/test-shape.c | 1 |
13 files changed, 624 insertions, 18 deletions
diff --git a/docs/pango-sections.txt b/docs/pango-sections.txt index 5050a1f3..51e85095 100644 --- a/docs/pango-sections.txt +++ b/docs/pango-sections.txt @@ -415,6 +415,8 @@ pango_color_parse pango_color_copy pango_color_free pango_color_to_string +PangoShowFlags +pango_attr_show_new <SUBSECTION> PangoAttrList PANGO_TYPE_ATTR_LIST diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c index 4db959fd..9acd6cf8 100644 --- a/pango/pango-attributes.c +++ b/pango/pango-attributes.c @@ -1206,6 +1206,31 @@ pango_attr_allow_breaks_new (gboolean allow_breaks) return pango_attr_int_new (&klass, (int)allow_breaks); } +/** + * pango_attr_show_new: + * @show: #PangoShowFlags to apply + * + * Create a new attribute that influences how invisible + * characters are rendered. + * + * Return value: (transfer full): the newly allocated #PangoAttribute, + * which should be freed with pango_attribute_destroy(). + * + * Since: 1.44 + **/ +PangoAttribute * +pango_attr_show_new (PangoShowFlags flags) +{ + static const PangoAttrClass klass = { + PANGO_ATTR_SHOW, + pango_attr_int_copy, + pango_attr_int_destroy, + pango_attr_int_equal, + }; + + return pango_attr_int_new (&klass, (int)flags); +} + /* * Attribute List */ diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h index f7a0c1cb..d46ce6f0 100644 --- a/pango/pango-attributes.h +++ b/pango/pango-attributes.h @@ -148,6 +148,7 @@ typedef struct _PangoAttrIterator PangoAttrIterator; * @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 + * @PANGO_ATTR_SHOW: how to render invisible characters (#PangoAttrInt). Since 1.44 * * The #PangoAttrType * distinguishes between different types of attributes. Along with the @@ -184,7 +185,8 @@ typedef enum PANGO_ATTR_FONT_FEATURES, /* PangoAttrString */ PANGO_ATTR_FOREGROUND_ALPHA, /* PangoAttrInt */ PANGO_ATTR_BACKGROUND_ALPHA, /* PangoAttrInt */ - PANGO_ATTR_ALLOW_BREAKS /* PangoAttrInt */ + PANGO_ATTR_ALLOW_BREAKS, /* PangoAttrInt */ + PANGO_ATTR_SHOW, /* PangoAttrInt */ } PangoAttrType; /** @@ -527,6 +529,27 @@ PangoAttribute *pango_attr_background_alpha_new (guint16 alpha); PANGO_AVAILABLE_IN_1_44 PangoAttribute *pango_attr_allow_breaks_new (gboolean allow_breaks); +/** + * PangoShowFlags: + * @PANGO_SHOW_NONE: No special treatment for invisible characters + * @PANGO_SHOW_SPACES: Render spaces, tabs and newlines visibly + * @PANGO_SHOW_LINE_BREAKS: Render line breaks visibly + * @PANGO_SHOW_IGNORABLES: Render default-ignorable Unicode + * characters visibly + * + * These flags affect how Pango treats characters that are normally + * not visible in the output. + */ +typedef enum { + PANGO_SHOW_NONE = 0, + PANGO_SHOW_SPACES = 1 << 0, + PANGO_SHOW_LINE_BREAKS = 1 << 1, + PANGO_SHOW_IGNORABLES = 1 << 2 +} PangoShowFlags; + +PANGO_AVAILABLE_IN_1_44 +PangoAttribute *pango_attr_show_new (PangoShowFlags flags); + PANGO_AVAILABLE_IN_ALL GType pango_attr_list_get_type (void) G_GNUC_CONST; PANGO_AVAILABLE_IN_ALL diff --git a/pango/pango-impl-utils.h b/pango/pango-impl-utils.h index 9570da80..378f14a1 100644 --- a/pango/pango-impl-utils.h +++ b/pango/pango-impl-utils.h @@ -128,6 +128,76 @@ pango_glyph_string_reverse_range (PangoGlyphString *glyphs, } } +/* The cairo hexbox drawing code assumes + * that these nicks are 1-6 ASCII chars + */ +static struct { + gunichar ch; + const char *nick; +} ignorables[] = { + { 0x00ad, "SHY" }, /* SOFT HYPHEN */ + { 0x034f, "CGJ" }, /* COMBINING GRAPHEME JOINER */ + { 0x200b, "ZWS" }, /* ZERO WIDTH SPACE */ + { 0x200c, "ZWNJ" }, /* ZERO WIDTH NON-JOINER */ + { 0x200d, "ZWJ" }, /* ZERO WIDTH JOINER */ + { 0x200e, "LRM" }, /* LEFT-TO-RIGHT MARK */ + { 0x200f, "RLM" }, /* RIGHT-TO-LEFT MARK */ + { 0x2028, "LS" }, /* LINE SEPARATOR */ + { 0x2029, "PS" }, /* PARAGRAPH SEPARATOR */ + { 0x202a, "LRE" }, /* LEFT-TO-RIGHT EMBEDDING */ + { 0x202b, "RLE" }, /* RIGHT-TO-LEFT EMBEDDING */ + { 0x202c, "PDF" }, /* POP DIRECTIONAL FORMATTING */ + { 0x202d, "LRO" }, /* LEFT-TO-RIGHT OVERRIDE */ + { 0x202e, "RLO" }, /* RIGHT-TO-LEFT OVERRIDE */ + { 0x2060, "WJ" }, /* WORD JOINER */ + { 0x2061, "FA" }, /* FUNCTION APPLICATION */ + { 0x2062, "IT" }, /* INVISIBLE TIMES */ + { 0x2063, "IS" }, /* INVISIBLE SEPARATOR */ + { 0xfeff, "ZWNBS" }, /* ZERO WIDTH NO-BREAK SPACE */ +}; + +static inline G_GNUC_UNUSED const char * +pango_get_ignorable (gunichar ch) +{ + for (int i = 0; i < G_N_ELEMENTS (ignorables); i++) + { + if (ch == ignorables[i].ch) + return ignorables[i].nick; + } + return NULL; +} + +static inline G_GNUC_UNUSED const char * +pango_get_ignorable_size (gunichar ch, + int *rows, + int *cols) +{ + const char *nick; + int len; + + nick = pango_get_ignorable (ch); + if (nick) + { + len = strlen (nick); + if (len < 4) + { + *rows = 1; + *cols = len; + } + else if (len > 4) + { + *rows = 2; + *cols = 3; + } + else + { + *rows = 2; + *cols = 2; + } + } + + return nick; +} G_END_DECLS diff --git a/pango/pango-layout.c b/pango/pango-layout.c index e7db7563..1cc1100c 100644 --- a/pango/pango-layout.c +++ b/pango/pango-layout.c @@ -2975,6 +2975,7 @@ pango_layout_line_leaked (PangoLayoutLine *line) *****************/ static void shape_tab (PangoLayoutLine *line, + PangoItem *item, PangoGlyphString *glyphs); static void @@ -3182,8 +3183,26 @@ line_width (PangoLayoutLine *line) return width; } +static gboolean +showing_space (const PangoAnalysis *analysis) +{ + GSList *l; + + for (l = analysis->extra_attrs; l; l = l->next) + { + PangoAttribute *attr = l->data; + + if (attr->klass->type == PANGO_ATTR_SHOW && + (((PangoAttrInt*)attr)->value & PANGO_SHOW_SPACES) != 0) + return TRUE; + } + + return FALSE; +} + static void shape_tab (PangoLayoutLine *line, + PangoItem *item, PangoGlyphString *glyphs) { int i, space_width; @@ -3192,7 +3211,10 @@ shape_tab (PangoLayoutLine *line, pango_glyph_string_set_size (glyphs, 1); - glyphs->glyphs[0].glyph = PANGO_GLYPH_EMPTY; + if (showing_space (&item->analysis)) + glyphs->glyphs[0].glyph = PANGO_GET_UNKNOWN_GLYPH ('\t'); + else + glyphs->glyphs[0].glyph = PANGO_GLYPH_EMPTY; glyphs->glyphs[0].geometry.x_offset = 0; glyphs->glyphs[0].geometry.y_offset = 0; glyphs->glyphs[0].attr.is_cluster_start = 1; @@ -3329,7 +3351,7 @@ shape_run (PangoLayoutLine *line, PangoGlyphString *glyphs = pango_glyph_string_new (); if (layout->text[item->offset] == '\t') - shape_tab (line, glyphs); + shape_tab (line, item, glyphs); else { if (state->properties.shape_set) @@ -3977,6 +3999,12 @@ pango_layout_get_effective_attributes (PangoLayout *layout) pango_attr_list_insert_before (attrs, attr); } + if (layout->single_paragraph) + { + PangoAttribute *attr = pango_attr_show_new (PANGO_SHOW_LINE_BREAKS); + pango_attr_list_insert_before (attrs, attr); + } + return attrs; } @@ -4022,6 +4050,7 @@ affects_break_or_shape (PangoAttribute *attr, case PANGO_ATTR_ALLOW_BREAKS: /* Affects shaping */ case PANGO_ATTR_FONT_FEATURES: + case PANGO_ATTR_SHOW: return TRUE; default: return FALSE; @@ -5300,6 +5329,9 @@ zero_line_final_space (PangoLayoutLine *line, PangoGlyphString *glyphs = run->glyphs; int glyph = item->analysis.level % 2 ? 0 : glyphs->num_glyphs - 1; + if (glyphs->glyphs[glyph].glyph == PANGO_GET_UNKNOWN_GLYPH (0x2028)) + return; /* this LS is visible */ + /* if the final char of line forms a cluster, and it's * a whitespace char, zero its glyph's width as it's been wrapped */ @@ -5314,6 +5346,7 @@ zero_line_final_space (PangoLayoutLine *line, state->remaining_width += glyphs->glyphs[glyph].geometry.width; glyphs->glyphs[glyph].geometry.width = 0; + glyphs->glyphs[glyph].glyph = PANGO_GLYPH_EMPTY; } /* When doing shaping, we add the letter spacing value for a diff --git a/pango/pango-markup.c b/pango/pango-markup.c index 2b871584..e39730e4 100644 --- a/pango/pango-markup.c +++ b/pango/pango-markup.c @@ -158,6 +158,11 @@ * not allowed, the range will be kept in a single run as far * as possible. Breaks are allowed by default. * + * show + * : A value determining how invisible characters are treated. + * Possible values are 'spaces', 'line-breaks', 'ignorables' + * or combinations, such as 'spaces|line-breaks'. + * * lang * : A language code, indicating the text language * @@ -1270,6 +1275,32 @@ span_parse_enum (const char *attr_name, } static gboolean +span_parse_flags (const char *attr_name, + const char *attr_val, + GType type, + int *val, + int line_number, + GError **error) +{ + char *possible_values = NULL; + + if (!pango_parse_flags (type, attr_val, val, &possible_values)) + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + _("'%s' is not a valid value for the '%s' " + "attribute on <span> tag, line %d; valid " + "values are %s or combinations with |"), + attr_val, attr_name, line_number, possible_values); + g_free (possible_values); + return FALSE; + } + + return TRUE; +} + +static gboolean span_parse_func (MarkupData *md G_GNUC_UNUSED, OpenTag *tag, const gchar **names, @@ -1303,6 +1334,7 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, const char *alpha = NULL; const char *background_alpha = NULL; const char *allow_breaks = NULL; + const char *show = NULL; g_markup_parse_context_get_position (context, &line_number, &char_number); @@ -1364,6 +1396,7 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, CHECK_ATTRIBUTE (font_features); break; case 's': + CHECK_ATTRIBUTE (show); CHECK_ATTRIBUTE (size); CHECK_ATTRIBUTE (stretch); CHECK_ATTRIBUTE (strikethrough); @@ -1683,6 +1716,16 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, add_attribute (tag, pango_attr_fallback_new (b)); } + if (G_UNLIKELY (show)) + { + PangoShowFlags flags; + + if (!span_parse_flags ("show", show, PANGO_TYPE_SHOW_FLAGS, (int*)(void*)&flags, line_number, error)) + goto error; + + add_attribute (tag, pango_attr_show_new (flags)); + } + if (G_UNLIKELY (rise)) { gint n = 0; diff --git a/pango/pango-utils-internal.h b/pango/pango-utils-internal.h index d6e55d7c..56340215 100644 --- a/pango/pango-utils-internal.h +++ b/pango/pango-utils-internal.h @@ -36,6 +36,10 @@ gboolean _pango_parse_enum (GType type, int *value, gboolean warn, char **possible_values); +gboolean pango_parse_flags (GType type, + const char *str, + int *value, + char **possible_values); char *_pango_trim_string (const char *str); diff --git a/pango/pango-utils.c b/pango/pango-utils.c index 5fc4475d..6f48c62c 100644 --- a/pango/pango-utils.c +++ b/pango/pango-utils.c @@ -772,6 +772,68 @@ _pango_parse_enum (GType type, return ret; } +gboolean +pango_parse_flags (GType type, + const char *str, + int *value, + char **possible_values) +{ + GFlagsClass *class = NULL; + gboolean ret = TRUE; + GFlagsValue *v = NULL; + + class = g_type_class_ref (type); + + v = g_flags_get_value_by_nick (class, str); + + if (v) + { + *value = v->value; + } + else if (!parse_int (str, value)) + { + char **strv = g_strsplit (str, "|", 0); + int i; + + *value = 0; + + for (i = 0; strv[i]; i++) + { + strv[i] = g_strstrip (strv[i]); + v = g_flags_get_value_by_nick (class, strv[i]); + if (!v) + { + ret = FALSE; + break; + } + *value |= v->value; + } + g_strfreev (strv); + + if (!ret && possible_values) + { + int i; + GString *s = g_string_new (NULL); + + for (i = 0; i < class->n_values; i++) + { + v = &class->values[i]; + if (i) + g_string_append_c (s, '/'); + g_string_append (s, v->value_nick); + } + + *possible_values = s->str; + + g_string_free (s, FALSE); + } + } + + g_type_class_unref (class); + + return ret; +} + /** * pango_lookup_aliases: * @fontname: an ascii string diff --git a/pango/pangocairo-font.c b/pango/pangocairo-font.c index 8b1f12d3..a49d14d4 100644 --- a/pango/pangocairo-font.c +++ b/pango/pangocairo-font.c @@ -720,7 +720,9 @@ _pango_cairo_font_private_get_glyph_extents_missing (PangoCairoFontPrivate *cf_p gunichar ch; gint rows, cols; - if (glyph == (0x20 | PANGO_GLYPH_UNKNOWN_FLAG)) + ch = glyph & ~PANGO_GLYPH_UNKNOWN_FLAG; + + if (ch == 0x20 || ch == 0x2423) { get_space_extents (cf_priv, ink_rect, logical_rect); return; @@ -733,13 +735,20 @@ _pango_cairo_font_private_get_glyph_extents_missing (PangoCairoFontPrivate *cf_p return; } - ch = glyph & ~PANGO_GLYPH_UNKNOWN_FLAG; - - rows = hbi->rows; if (G_UNLIKELY (glyph == PANGO_GLYPH_INVALID_INPUT || ch > 0x10FFFF)) - cols = 1; + { + rows = hbi->rows; + cols = 1; + } + else if (pango_get_ignorable_size (ch, &rows, &cols)) + { + /* We special-case ignorables when rendering hex boxes */ + } else - cols = ((glyph & ~PANGO_GLYPH_UNKNOWN_FLAG) > 0xffff ? 6 : 4) / rows; + { + rows = hbi->rows; + cols = ((glyph & ~PANGO_GLYPH_UNKNOWN_FLAG) > 0xffff ? 6 : 4) / rows; + } if (ink_rect) { diff --git a/pango/pangocairo-render.c b/pango/pangocairo-render.c index 3db3de5e..f4c57a87 100644 --- a/pango/pangocairo-render.c +++ b/pango/pangocairo-render.c @@ -159,6 +159,7 @@ #include "pango-font-private.h" #include "pangocairo-private.h" #include "pango-glyph-item.h" +#include "pango-impl-utils.h" typedef struct _PangoCairoRendererClass PangoCairoRendererClass; @@ -369,10 +370,12 @@ _pango_cairo_renderer_draw_unknown_glyph (PangoCairoRenderer *crenderer, int row, col; int rows, cols; double width, lsb; - char hexbox_string[2] = {0, 0}; + char hexbox_string[2] = { 0, 0 }; PangoCairoFontHexBoxInfo *hbi; gunichar ch; gboolean invalid_input; + char *p; + const char *name; cairo_save (crenderer->cr); @@ -386,15 +389,100 @@ _pango_cairo_renderer_draw_unknown_glyph (PangoCairoRenderer *crenderer, goto done; } - rows = hbi->rows; if (G_UNLIKELY (invalid_input)) { + rows = hbi->rows; cols = 1; } + else if (ch == 0x2423 || + g_unichar_type (ch) == G_UNICODE_SPACE_SEPARATOR) + { + /* We never want to show a hex box or other drawing for + * space. If we want space to be visible, we replace 0x20 + * by 0x2423 (visible space). + * + * Since we don't want to rely on glyph availability, + * we render a centered dot ourselves. + */ + double x = cx + 0.5 *((double)gi->geometry.width / PANGO_SCALE); + double y = cy + hbi->box_descent - 0.5 * hbi->box_height; + + cairo_new_sub_path (crenderer->cr); + cairo_arc (crenderer->cr, x, y, 1.5 * hbi->line_width, 0, 2 * G_PI); + cairo_close_path (crenderer->cr); + cairo_fill (crenderer->cr); + goto done; + } + else if (ch == '\t') + { + /* Since we don't want to rely on glyph availability, + * we render an arrow like ↦ ourselves. + */ + double y = cy + hbi->box_descent - 0.5 * hbi->box_height; + double width = (double)gi->geometry.width / PANGO_SCALE; + double offset = 0.2 * width; + double x = cx + offset; + double al = width - 2 * offset; /* arrow length */ + double tl = MIN (hbi->digit_width, 0.75 * al); /* tip length */ + double tw2 = 2.5 * hbi->line_width; /* tip width / 2 */ + double lw2 = 0.5 * hbi->line_width; /* line width / 2 */ + + cairo_move_to (crenderer->cr, x - lw2, y - tw2); + cairo_line_to (crenderer->cr, x + lw2, y - tw2); + cairo_line_to (crenderer->cr, x + lw2, y - lw2); + cairo_line_to (crenderer->cr, x + al - tl, y - lw2); + cairo_line_to (crenderer->cr, x + al - tl, y - tw2); + cairo_line_to (crenderer->cr, x + al, y); + cairo_line_to (crenderer->cr, x + al - tl, y + tw2); + cairo_line_to (crenderer->cr, x + al - tl, y + lw2); + cairo_line_to (crenderer->cr, x + lw2, y + lw2); + cairo_line_to (crenderer->cr, x + lw2, y + tw2); + cairo_line_to (crenderer->cr, x - lw2, y + tw2); + cairo_close_path (crenderer->cr); + cairo_fill (crenderer->cr); + goto done; + } + else if (ch == '\n' || ch == 0x2028 || ch == 0x2029) + { + /* Since we don't want to rely on glyph availability, + * we render an arrow like ↵ ourselves. + */ + double width = (double)gi->geometry.width / PANGO_SCALE; + double offset = 0.2 * width; + double al = width - 2 * offset; /* arrow length */ + double tl = MIN (hbi->digit_width, 0.75 * al); /* tip length */ + double ah = al - 0.5 * tl; /* arrow height */ + double tw2 = 2.5 * hbi->line_width; /* tip width / 2 */ + double x = cx + offset; + double y = cy - (hbi->box_height - al) / 2; + double lw2 = 0.5 * hbi->line_width; /* line width / 2 */ + + cairo_move_to (crenderer->cr, x, y); + cairo_line_to (crenderer->cr, x + tl, y - tw2); + cairo_line_to (crenderer->cr, x + tl, y - lw2); + cairo_line_to (crenderer->cr, x + al - lw2, y - lw2); + cairo_line_to (crenderer->cr, x + al - lw2, y - ah); + cairo_line_to (crenderer->cr, x + al + lw2, y - ah); + cairo_line_to (crenderer->cr, x + al + lw2, y + lw2); + cairo_line_to (crenderer->cr, x + tl, y + lw2); + cairo_line_to (crenderer->cr, x + tl, y + tw2); + cairo_close_path (crenderer->cr); + cairo_fill (crenderer->cr); + goto done; + } + else if ((name = pango_get_ignorable_size (ch, &rows, &cols))) + { + /* Nothing else to do, we render 'default ignorable' chars + * as hex box with their nick. + */ + } else { + /* Everything else gets a traditional hex box. */ + rows = hbi->rows; cols = (ch > 0xffff ? 6 : 4) / rows; g_snprintf (buf, sizeof(buf), (ch > 0xffff) ? "%06X" : "%04X", ch); + name = buf; } width = (3 * hbi->pad_x + cols * (hbi->digit_width + hbi->pad_x)); @@ -413,18 +501,21 @@ _pango_cairo_renderer_draw_unknown_glyph (PangoCairoRenderer *crenderer, goto done; x0 = cx + lsb + hbi->pad_x * 2; - y0 = cy + hbi->box_descent - hbi->pad_y * 2; + y0 = cy + hbi->box_descent - hbi->pad_y * 2 - ((hbi->rows - rows) * hbi->digit_height / 2); - for (row = 0; row < rows; row++) + for (row = 0, p = name; row < rows; row++) { double y = y0 - (rows - 1 - row) * (hbi->digit_height + hbi->pad_y); - for (col = 0; col < cols; col++) + for (col = 0; col < cols; col++, p++) { double x = x0 + col * (hbi->digit_width + hbi->pad_x); + if (!p) + goto done; + cairo_move_to (crenderer->cr, x, y); - hexbox_string[0] = buf[row * cols + col]; + hexbox_string[0] = p[0]; if (crenderer->do_path) cairo_text_path (crenderer->cr, hexbox_string); diff --git a/pango/pangofc-shape.c b/pango/pangofc-shape.c index 12ce7ddb..c2b8dc3e 100644 --- a/pango/pangofc-shape.c +++ b/pango/pangofc-shape.c @@ -112,6 +112,239 @@ apply_extra_attributes (GSList *attrs, } } +typedef struct +{ + PangoFont *font; + hb_font_t *parent; + PangoShowFlags show_flags; +} PangoHbShapeContext; + +static hb_bool_t +pango_hb_font_get_nominal_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data G_GNUC_UNUSED) +{ + PangoHbShapeContext *context = (PangoHbShapeContext *) font_data; + + if ((context->show_flags & PANGO_SHOW_IGNORABLES) != 0) + { + if (pango_get_ignorable (unicode)) + { + *glyph = PANGO_GET_UNKNOWN_GLYPH (unicode); + return TRUE; + } + } + + if ((context->show_flags & PANGO_SHOW_SPACES) != 0) + { + if (g_unichar_type (unicode) == G_UNICODE_SPACE_SEPARATOR) + { + /* Replace 0x20 by visible space, since we + * don't draw a hex box for 0x20 + */ + if (unicode == 0x20) + *glyph = PANGO_GET_UNKNOWN_GLYPH (0x2423); + else + *glyph = PANGO_GET_UNKNOWN_GLYPH (unicode); + return TRUE; + } + } + + if ((context->show_flags & PANGO_SHOW_LINE_BREAKS) != 0) + { + if (unicode == 0x2028) + { + /* Always mark LS as unknown. If it ends up + * at the line end, PangoLayout takes care of + * hiding them, and if they end up in the middle + * of a line, we are in single paragraph mode + * and want to show the LS + */ + *glyph = PANGO_GET_UNKNOWN_GLYPH (unicode); + return TRUE; + } + } + + if (hb_font_get_glyph (context->parent, unicode, 0, glyph)) + return TRUE; + + *glyph = PANGO_GET_UNKNOWN_GLYPH (unicode); + + /* We draw our own invalid-Unicode shape, so prevent HarfBuzz + * from using REPLACEMENT CHARACTER. */ + if (unicode > 0x10FFFF) + return TRUE; + + return FALSE; +} + +static hb_bool_t +pango_hb_font_get_variation_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data G_GNUC_UNUSED) +{ + PangoHbShapeContext *context = (PangoHbShapeContext *) font_data; + + if (hb_font_get_glyph (context->parent, + unicode, variation_selector, glyph)) + return TRUE; + + return FALSE; +} + +static hb_bool_t +pango_hb_font_get_glyph_contour_point (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + unsigned int point_index, + hb_position_t *x, + hb_position_t *y, + void *user_data G_GNUC_UNUSED) +{ + PangoHbShapeContext *context = (PangoHbShapeContext *) font_data; + + return hb_font_get_glyph_contour_point (context->parent, glyph, point_index, x, y); +} + +static hb_position_t +pango_hb_font_get_glyph_advance (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + void *user_data G_GNUC_UNUSED) +{ + PangoHbShapeContext *context = (PangoHbShapeContext *) font_data; + + if (glyph & PANGO_GLYPH_UNKNOWN_FLAG) + { + PangoRectangle logical; + + pango_font_get_glyph_extents (context->font, glyph, NULL, &logical); + return logical.width; + } + + return hb_font_get_glyph_h_advance (context->parent, glyph); +} + +static hb_bool_t +pango_hb_font_get_glyph_extents (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data G_GNUC_UNUSED) +{ + PangoHbShapeContext *context = (PangoHbShapeContext *) font_data; + + if (glyph & PANGO_GLYPH_UNKNOWN_FLAG) + { + PangoRectangle ink; + + pango_font_get_glyph_extents (context->font, glyph, &ink, NULL); + + extents->x_bearing = ink.x; + extents->y_bearing = ink.y; + extents->width = ink.width; + extents->height = ink.height; + + return TRUE; + } + + return hb_font_get_glyph_extents (context->parent, glyph, extents); +} + +static hb_bool_t +pango_hb_font_get_glyph_h_origin (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data G_GNUC_UNUSED) +{ + PangoHbShapeContext *context = (PangoHbShapeContext *) font_data; + + return hb_font_get_glyph_h_origin (context->parent, glyph, x, y); +} + +static hb_bool_t +pango_hb_font_get_glyph_v_origin (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, +void *user_data G_GNUC_UNUSED) +{ + PangoHbShapeContext *context = (PangoHbShapeContext *) font_data; + + return hb_font_get_glyph_v_origin (context->parent, glyph, x, y); +} + +static hb_position_t +pango_hb_font_get_h_kerning (hb_font_t *font, + void *font_data, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph, + void *user_data G_GNUC_UNUSED) +{ + PangoHbShapeContext *context = (PangoHbShapeContext *) font_data; + + return hb_font_get_glyph_h_kerning (context->parent, left_glyph, right_glyph); +} + +static hb_font_t * +pango_font_get_hb_font_for_context (PangoFont *font, + PangoHbShapeContext *context) +{ + hb_font_t *hb_font; + static hb_font_funcs_t *funcs; + + hb_font = pango_font_get_hb_font (font); + if (context->show_flags == PANGO_SHOW_NONE) + return hb_font_reference (hb_font); + + if (G_UNLIKELY (!funcs)) + { + funcs = hb_font_funcs_create (); + hb_font_funcs_set_nominal_glyph_func (funcs, pango_hb_font_get_nominal_glyph, NULL, NULL); + hb_font_funcs_set_variation_glyph_func (funcs, pango_hb_font_get_variation_glyph, NULL, NULL); + hb_font_funcs_set_glyph_h_advance_func (funcs, pango_hb_font_get_glyph_advance, NULL, NULL); + hb_font_funcs_set_glyph_v_advance_func (funcs, pango_hb_font_get_glyph_advance, NULL, NULL); + hb_font_funcs_set_glyph_h_origin_func (funcs, pango_hb_font_get_glyph_h_origin, NULL, NULL); + hb_font_funcs_set_glyph_v_origin_func (funcs, pango_hb_font_get_glyph_v_origin, NULL, NULL); + hb_font_funcs_set_glyph_h_kerning_func (funcs, pango_hb_font_get_h_kerning, NULL, NULL); + hb_font_funcs_set_glyph_extents_func (funcs, pango_hb_font_get_glyph_extents, NULL, NULL); + hb_font_funcs_set_glyph_contour_point_func (funcs, pango_hb_font_get_glyph_contour_point, NULL, NULL); + } + + context->font = font; + context->parent = hb_font; + + hb_font = hb_font_create_sub_font (hb_font); + hb_font_set_funcs (hb_font, funcs, context, NULL); + + return hb_font; +} + +static PangoShowFlags +find_show_flags (const PangoAnalysis *analysis) +{ + GSList *l; + PangoShowFlags flags = 0; + + for (l = analysis->extra_attrs; l; l = l->next) + { + PangoAttribute *attr = l->data; + + if (attr->klass->type == PANGO_ATTR_SHOW) + flags |= ((PangoAttrInt*)attr)->value; + } + + return flags; +} + void pango_hb_shape (PangoFont *font, const char *item_text, @@ -121,6 +354,8 @@ pango_hb_shape (PangoFont *font, const char *paragraph_text, unsigned int paragraph_length) { + PangoHbShapeContext context; + hb_buffer_flags_t hb_buffer_flags; hb_font_t *hb_font; hb_buffer_t *hb_buffer; hb_direction_t hb_direction; @@ -137,8 +372,8 @@ pango_hb_shape (PangoFont *font, g_return_if_fail (font != NULL); g_return_if_fail (analysis != NULL); - hb_font = pango_font_get_hb_font (font); - + context.show_flags = find_show_flags (analysis); + hb_font = pango_font_get_hb_font_for_context (font, &context); hb_buffer = acquire_buffer (&free_buffer); hb_direction = PANGO_GRAVITY_IS_VERTICAL (analysis->gravity) ? HB_DIRECTION_TTB : HB_DIRECTION_LTR; @@ -147,6 +382,11 @@ pango_hb_shape (PangoFont *font, if (PANGO_GRAVITY_IS_IMPROPER (analysis->gravity)) hb_direction = HB_DIRECTION_REVERSE (hb_direction); + hb_buffer_flags = HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT; + + if (context.show_flags & PANGO_SHOW_IGNORABLES) + hb_buffer_flags |= HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES; + /* setup buffer */ hb_buffer_set_direction (hb_buffer, hb_direction); @@ -155,7 +395,8 @@ pango_hb_shape (PangoFont *font, #if HB_VERSION_ATLEAST(1,0,3) hb_buffer_set_cluster_level (hb_buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); #endif - hb_buffer_set_flags (hb_buffer, HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT); + hb_buffer_set_flags (hb_buffer, hb_buffer_flags); + hb_buffer_set_invisible_glyph (hb_buffer, PANGO_GLYPH_EMPTY); hb_buffer_add_utf8 (hb_buffer, paragraph_text, paragraph_length, item_offset, item_length); if (analysis->flags & PANGO_ANALYSIS_FLAG_NEED_HYPHEN) @@ -216,4 +457,5 @@ pango_hb_shape (PangoFont *font, } release_buffer (hb_buffer, free_buffer); + hb_font_destroy (hb_font); } diff --git a/tests/test-common.c b/tests/test-common.c index 0b6c5c42..3b1620ed 100644 --- a/tests/test-common.c +++ b/tests/test-common.c @@ -119,6 +119,7 @@ print_attribute (PangoAttribute *attr, GString *string) case PANGO_ATTR_FOREGROUND_ALPHA: case PANGO_ATTR_BACKGROUND_ALPHA: case PANGO_ATTR_ALLOW_BREAKS: + case PANGO_ATTR_SHOW: g_string_append_printf (string, "%d", ((PangoAttrInt *)attr)->value); break; case PANGO_ATTR_FONT_DESC: diff --git a/tests/test-shape.c b/tests/test-shape.c index 898c4a84..8c84ab0a 100644 --- a/tests/test-shape.c +++ b/tests/test-shape.c @@ -113,6 +113,7 @@ affects_break_or_shape (PangoAttribute *attr, case PANGO_ATTR_ALLOW_BREAKS: /* Affects shaping */ case PANGO_ATTR_FONT_FEATURES: + case PANGO_ATTR_SHOW: return TRUE; default: return FALSE; |