summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2021-08-20 17:54:31 -0400
committerMatthias Clasen <mclasen@redhat.com>2021-08-20 23:16:48 -0400
commite892e6e93a1d4bb7ae2a921c006a8acdbccaae13 (patch)
tree2dfa66e98593559172be86772e79c570dcf98ecf
parent585a90214a9207ffd6969e515f85d13f0f472771 (diff)
downloadpango-e892e6e93a1d4bb7ae2a921c006a8acdbccaae13.tar.gz
shaping: implement text transform
Transform the text we hand to harfbuzz, as prescribed by the text transform attributes we have. This uses the log attrs to find word starts.
-rw-r--r--pango/shape.c248
1 files changed, 167 insertions, 81 deletions
diff --git a/pango/shape.c b/pango/shape.c
index 23325bbf..8dbdfdd6 100644
--- a/pango/shape.c
+++ b/pango/shape.c
@@ -31,8 +31,8 @@
#include "pango-font-private.h"
/* {{{ Harfbuzz shaping */
-
/* {{{{ Buffer handling */
+
static hb_buffer_t *cached_buffer = NULL; /* MT-safe */
G_LOCK_DEFINE_STATIC (cached_buffer);
@@ -70,6 +70,7 @@ release_buffer (hb_buffer_t *buffer,
else
hb_buffer_destroy (buffer);
}
+
/* }}}} */
/* {{{{ Use PangoFont with Harfbuzz */
@@ -234,6 +235,7 @@ pango_font_get_hb_font_for_context (PangoFont *font,
/* }}}} */
/* {{{{ Utilities */
+
static void
apply_extra_attributes (GSList *attrs,
hb_feature_t *features,
@@ -317,7 +319,24 @@ find_show_flags (const PangoAnalysis *analysis)
return flags;
}
+static PangoTextTransform
+find_text_transform (const PangoAnalysis *analysis)
+{
+ GSList *l;
+
+ for (l = analysis->extra_attrs; l; l = l->next)
+ {
+ PangoAttribute *attr = l->data;
+
+ if (attr->klass->type == PANGO_ATTR_TEXT_TRANSFORM)
+ return (PangoTextTransform) ((PangoAttrInt*)attr)->value;
+ }
+
+ return PANGO_TEXT_TRANSFORM_NONE;
+}
+
/* }}}} */
+
static void
pango_hb_shape (const char *item_text,
int item_length,
@@ -342,6 +361,7 @@ pango_hb_shape (const char *item_text,
hb_feature_t features[32];
unsigned int num_features = 0;
PangoGlyphInfo *infos;
+ PangoTextTransform transform;
g_return_if_fail (analysis != NULL);
g_return_if_fail (analysis->font != NULL);
@@ -350,6 +370,8 @@ pango_hb_shape (const char *item_text,
hb_font = pango_font_get_hb_font_for_context (analysis->font, &context);
hb_buffer = acquire_buffer (&free_buffer);
+ transform = find_text_transform (analysis);
+
hb_direction = PANGO_GRAVITY_IS_VERTICAL (analysis->gravity) ? HB_DIRECTION_TTB : HB_DIRECTION_LTR;
if (analysis->level % 2)
hb_direction = HB_DIRECTION_REVERSE (hb_direction);
@@ -370,7 +392,67 @@ pango_hb_shape (const char *item_text,
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 (transform == PANGO_TEXT_TRANSFORM_NONE)
+ {
+ hb_buffer_add_utf8 (hb_buffer, paragraph_text, paragraph_length, item_offset, item_length);
+ }
+ else
+ {
+ const char *p;
+ int i;
+
+ /* Add pre-context */
+ hb_buffer_add_utf8 (hb_buffer, paragraph_text, item_offset, item_offset, 0);
+
+ /* Transform the item text according to text transform.
+ * Note: we assume text transforms won't cross font boundaries
+ */
+ for (p = paragraph_text + item_offset, i = 0; p < paragraph_text + item_offset + item_length; p = g_utf8_next_char (p), i++)
+ {
+ int index = p - paragraph_text;
+ gunichar ch = g_utf8_get_char (p);
+ char *str = NULL;
+
+ switch (transform)
+ {
+ case PANGO_TEXT_TRANSFORM_LOWERCASE:
+ if (g_unichar_isalnum (ch))
+ str = g_utf8_strdown (p, g_utf8_next_char (p) - p);
+ break;
+
+ case PANGO_TEXT_TRANSFORM_UPPERCASE:
+ if (g_unichar_isalnum (ch))
+ str = g_utf8_strup (p, g_utf8_next_char (p) - p);
+ break;
+
+ case PANGO_TEXT_TRANSFORM_CAPITALIZE:
+ if (log_attrs[i].is_word_start)
+ ch = g_unichar_totitle (ch);
+ break;
+
+ case PANGO_TEXT_TRANSFORM_NONE:
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (str)
+ {
+ for (const char *q = str; *q; q = g_utf8_next_char (q))
+ {
+ ch = g_utf8_get_char (q);
+ hb_buffer_add (hb_buffer, ch, index);
+ }
+ g_free (str);
+ }
+ else
+ hb_buffer_add (hb_buffer, ch, index);
+ }
+
+ /* Add post-context */
+ hb_buffer_add_utf8 (hb_buffer, paragraph_text + item_offset + item_length, paragraph_length - (item_offset + item_length),
+ item_offset + item_length, 0);
+ }
+
if (analysis->flags & PANGO_ANALYSIS_FLAG_NEED_HYPHEN)
{
/* Insert either a Unicode or ASCII hyphen. We may
@@ -380,6 +462,7 @@ pango_hb_shape (const char *item_text,
int last_char_len = p - g_utf8_prev_char (p);
hb_codepoint_t glyph;
+ /* Note: We rely on hb_buffer_add clearing existing post-context */
if (hb_font_get_nominal_glyph (hb_font, 0x2010, &glyph))
hb_buffer_add (hb_buffer, 0x2010, item_offset + item_length - last_char_len);
else if (hb_font_get_nominal_glyph (hb_font, '-', &glyph))
@@ -486,85 +569,11 @@ fallback_shape (const char *text,
pango_glyph_string_reverse_range (glyphs, 0, glyphs->num_glyphs);
}
-/* }}} */
-/* {{{ Public API */
-
-/**
- * pango_shape:
- * @text: the text to process
- * @length: the length (in bytes) of @text
- * @analysis: `PangoAnalysis` structure from [func@itemize]
- * @glyphs: glyph string in which to store results
- *
- * Convert the characters in @text into glyphs.
- *
- * Given a segment of text and the corresponding `PangoAnalysis` structure
- * returned from [func@itemize], convert the characters into glyphs. You
- * may also pass in only a substring of the item from [func@itemize].
- *
- * It is recommended that you use [func@shape_full] instead, since
- * that API allows for shaping interaction happening across text item
- * boundaries.
- *
- * Note that the extra attributes in the @analyis that is returned from
- * [func@itemize] have indices that are relative to the entire paragraph,
- * so you need to subtract the item offset from their indices before
- * calling [func@shape].
- */
-void
-pango_shape (const char *text,
- int length,
- const PangoAnalysis *analysis,
- PangoGlyphString *glyphs)
-{
- pango_shape_full (text, length, text, length, analysis, glyphs);
-}
-
-/**
- * pango_shape_full:
- * @item_text: valid UTF-8 text to shape.
- * @item_length: the length (in bytes) of @item_text. -1 means nul-terminated text.
- * @paragraph_text: (nullable): text of the paragraph (see details). May be %NULL.
- * @paragraph_length: the length (in bytes) of @paragraph_text. -1 means nul-terminated text.
- * @analysis: `PangoAnalysis` structure from [func@itemize].
- * @glyphs: glyph string in which to store results.
- *
- * Convert the characters in @text into glyphs.
- *
- * Given a segment of text and the corresponding `PangoAnalysis` structure
- * returned from [func@itemize], convert the characters into glyphs. You may
- * also pass in only a substring of the item from [func@itemize].
- *
- * This is similar to [func@shape], except it also can optionally take
- * the full paragraph text as input, which will then be used to perform
- * certain cross-item shaping interactions. If you have access to the broader
- * text of which @item_text is part of, provide the broader text as
- * @paragraph_text. If @paragraph_text is %NULL, item text is used instead.
- *
- * Note that the extra attributes in the @analyis that is returned from
- * [func@itemize] have indices that are relative to the entire paragraph,
- * so you do not pass the full paragraph text as @paragraph_text, you need
- * to subtract the item offset from their indices before calling [func@shape_full].
- *
- * Since: 1.32
- */
-void
-pango_shape_full (const char *item_text,
- int item_length,
- const char *paragraph_text,
- int paragraph_length,
- const PangoAnalysis *analysis,
- PangoGlyphString *glyphs)
-{
- pango_shape_with_flags (item_text, item_length,
- paragraph_text, paragraph_length,
- analysis,
- glyphs,
- PANGO_SHAPE_NONE);
-}
+/* }}} */
+/* {{{ Shaping implementation */
static void
-pango_shape_with_all (const char *item_text,
+pango_shape_internal (const char *item_text,
int item_length,
const char *paragraph_text,
int paragraph_length,
@@ -740,6 +749,83 @@ pango_shape_with_all (const char *item_text,
}
}
+/* }}} */
+/* {{{ Public API */
+
+/**
+ * pango_shape:
+ * @text: the text to process
+ * @length: the length (in bytes) of @text
+ * @analysis: `PangoAnalysis` structure from [func@itemize]
+ * @glyphs: glyph string in which to store results
+ *
+ * Convert the characters in @text into glyphs.
+ *
+ * Given a segment of text and the corresponding `PangoAnalysis` structure
+ * returned from [func@itemize], convert the characters into glyphs. You
+ * may also pass in only a substring of the item from [func@itemize].
+ *
+ * It is recommended that you use [func@shape_full] instead, since
+ * that API allows for shaping interaction happening across text item
+ * boundaries.
+ *
+ * Note that the extra attributes in the @analyis that is returned from
+ * [func@itemize] have indices that are relative to the entire paragraph,
+ * so you need to subtract the item offset from their indices before
+ * calling [func@shape].
+ */
+void
+pango_shape (const char *text,
+ int length,
+ const PangoAnalysis *analysis,
+ PangoGlyphString *glyphs)
+{
+ pango_shape_full (text, length, text, length, analysis, glyphs);
+}
+
+/**
+ * pango_shape_full:
+ * @item_text: valid UTF-8 text to shape.
+ * @item_length: the length (in bytes) of @item_text. -1 means nul-terminated text.
+ * @paragraph_text: (nullable): text of the paragraph (see details). May be %NULL.
+ * @paragraph_length: the length (in bytes) of @paragraph_text. -1 means nul-terminated text.
+ * @analysis: `PangoAnalysis` structure from [func@itemize].
+ * @glyphs: glyph string in which to store results.
+ *
+ * Convert the characters in @text into glyphs.
+ *
+ * Given a segment of text and the corresponding `PangoAnalysis` structure
+ * returned from [func@itemize], convert the characters into glyphs. You may
+ * also pass in only a substring of the item from [func@itemize].
+ *
+ * This is similar to [func@shape], except it also can optionally take
+ * the full paragraph text as input, which will then be used to perform
+ * certain cross-item shaping interactions. If you have access to the broader
+ * text of which @item_text is part of, provide the broader text as
+ * @paragraph_text. If @paragraph_text is %NULL, item text is used instead.
+ *
+ * Note that the extra attributes in the @analyis that is returned from
+ * [func@itemize] have indices that are relative to the entire paragraph,
+ * so you do not pass the full paragraph text as @paragraph_text, you need
+ * to subtract the item offset from their indices before calling [func@shape_full].
+ *
+ * Since: 1.32
+ */
+void
+pango_shape_full (const char *item_text,
+ int item_length,
+ const char *paragraph_text,
+ int paragraph_length,
+ const PangoAnalysis *analysis,
+ PangoGlyphString *glyphs)
+{
+ pango_shape_with_flags (item_text, item_length,
+ paragraph_text, paragraph_length,
+ analysis,
+ glyphs,
+ PANGO_SHAPE_NONE);
+}
+
/**
* pango_shape_with_flags:
* @item_text: valid UTF-8 text to shape
@@ -779,7 +865,7 @@ pango_shape_with_flags (const char *item_text,
PangoGlyphString *glyphs,
PangoShapeFlags flags)
{
- pango_shape_with_all (item_text, item_length,
+ pango_shape_internal (item_text, item_length,
paragraph_text, paragraph_length,
analysis, NULL,
glyphs, flags);
@@ -818,7 +904,7 @@ pango_shape_item (PangoItem *item,
PangoGlyphString *glyphs,
PangoShapeFlags flags)
{
- pango_shape_with_all (paragraph_text + item->offset, item->length,
+ pango_shape_internal (paragraph_text + item->offset, item->length,
paragraph_text, paragraph_length,
&item->analysis, log_attrs,
glyphs, flags);