summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2019-07-25 04:54:05 +0000
committerMatthias Clasen <mclasen@redhat.com>2019-07-25 04:54:05 +0000
commit997c73409594ef8b434b6d1b3da6c75ae1fcf536 (patch)
treeace6d1c1dc75a37032d595076064b6ba3596a22c
parent201afb727c0ca2e428711d25f74d0f0e717da7de (diff)
parent49cd1c08f86ee29761190268071779959c36009b (diff)
downloadpango-997c73409594ef8b434b6d1b3da6c75ae1fcf536.tar.gz
Merge branch 'visible-things' into 'master'
Make things visible See merge request GNOME/pango!69
-rw-r--r--docs/pango-sections.txt2
-rw-r--r--pango/pango-attributes.c25
-rw-r--r--pango/pango-attributes.h25
-rw-r--r--pango/pango-impl-utils.h70
-rw-r--r--pango/pango-layout.c37
-rw-r--r--pango/pango-markup.c43
-rw-r--r--pango/pango-utils-internal.h4
-rw-r--r--pango/pango-utils.c62
-rw-r--r--pango/pangocairo-font.c21
-rw-r--r--pango/pangocairo-render.c103
-rw-r--r--pango/pangofc-shape.c248
-rw-r--r--tests/test-common.c1
-rw-r--r--tests/test-shape.c1
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;