summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2021-08-28 14:52:23 -0400
committerMatthias Clasen <mclasen@redhat.com>2021-08-30 00:03:00 -0400
commita89d6ab2716e1801647f3807c13fe5b9e20c21dc (patch)
tree167f68b3e516fdbc694e2240f61ea7a4faf0e1db
parent877d0bf4c93246fc8787b86316580fe52eecb9ed (diff)
downloadpango-a89d6ab2716e1801647f3807c13fe5b9e20c21dc.tar.gz
Add pango_glyph_string_x_to_index_full
Same as pango_glyph_string_index_to_full, it uses log attrs to disambiguate clusters a bit better.
-rw-r--r--pango/glyphstring.c300
-rw-r--r--pango/pango-glyph.h19
2 files changed, 226 insertions, 93 deletions
diff --git a/pango/glyphstring.c b/pango/glyphstring.c
index ea9a6398..00deeb8d 100644
--- a/pango/glyphstring.c
+++ b/pango/glyphstring.c
@@ -650,6 +650,44 @@ pango_glyph_string_x_to_index (PangoGlyphString *glyphs,
int *index,
gboolean *trailing)
{
+ pango_glyph_string_x_to_index_full (glyphs,
+ text, length,
+ analysis, NULL,
+ x_pos,
+ index, trailing);
+}
+
+/**
+ * pango_glyph_string_x_to_index_full:
+ * @glyphs: the glyphs returned from [func@shape]
+ * @text: the text for the run
+ * @length: the number of bytes (not characters) in text.
+ * @analysis: the analysis information return from [func@itemize]
+ * @attrs: (nullable): `PangoLogAttr` array for @text
+ * @x_pos: the x offset (in Pango units)
+ * @index_: (out): location to store calculated byte index within @text
+ * @trailing: (out): location to store a boolean indicating whether the
+ * user clicked on the leading or trailing edge of the character
+ *
+ * Convert from x offset to character position.
+ *
+ * This variant of [method@Pango.GlyphString.x_to_index] additionally
+ * accepts a `PangoLogAttr` array. The grapheme boundary information
+ * in it can be used to disambiguate positioning inside some complex
+ * clusters.
+ *
+ * Since: 1.50
+ */
+void
+pango_glyph_string_x_to_index_full (PangoGlyphString *glyphs,
+ const char *text,
+ int length,
+ PangoAnalysis *analysis,
+ PangoLogAttr *attrs,
+ int x_pos,
+ int *index,
+ gboolean *trailing)
+{
int i;
int start_xpos = 0;
int end_xpos = 0;
@@ -658,9 +696,17 @@ pango_glyph_string_x_to_index (PangoGlyphString *glyphs,
int start_index = -1;
int end_index = -1;
+ int start_glyph_pos = 0;
+ int end_glyph_pos = 0;
+
int cluster_chars = 0;
const char *p;
+ int start_offset;
+
+ hb_position_t caret[16];
+ unsigned int caret_count = 16;
+
gboolean found = FALSE;
/* Find the cluster containing the position */
@@ -670,55 +716,59 @@ pango_glyph_string_x_to_index (PangoGlyphString *glyphs,
if (analysis->level % 2) /* Right to left */
{
for (i = glyphs->num_glyphs - 1; i >= 0; i--)
- width += glyphs->glyphs[i].geometry.width;
+ width += glyphs->glyphs[i].geometry.width;
for (i = glyphs->num_glyphs - 1; i >= 0; i--)
- {
- if (glyphs->log_clusters[i] != start_index)
- {
- if (found)
- {
- end_index = glyphs->log_clusters[i];
- end_xpos = width;
- break;
- }
- else
- {
- start_index = glyphs->log_clusters[i];
- start_xpos = width;
- }
- }
+ {
+ if (glyphs->log_clusters[i] != start_index)
+ {
+ if (found)
+ {
+ end_index = glyphs->log_clusters[i];
+ end_xpos = width;
+ end_glyph_pos = i;
+ break;
+ }
+ else
+ {
+ start_index = glyphs->log_clusters[i];
+ start_xpos = width;
+ start_glyph_pos = i;
+ }
+ }
- width -= glyphs->glyphs[i].geometry.width;
+ width -= glyphs->glyphs[i].geometry.width;
- if (width <= x_pos && x_pos < width + glyphs->glyphs[i].geometry.width)
- found = TRUE;
- }
+ if (width <= x_pos && x_pos < width + glyphs->glyphs[i].geometry.width)
+ found = TRUE;
+ }
}
else /* Left to right */
{
for (i = 0; i < glyphs->num_glyphs; i++)
- {
- if (glyphs->log_clusters[i] != start_index)
- {
- if (found)
- {
- end_index = glyphs->log_clusters[i];
- end_xpos = width;
- break;
- }
- else
- {
- start_index = glyphs->log_clusters[i];
- start_xpos = width;
- }
- }
+ {
+ if (glyphs->log_clusters[i] != start_index)
+ {
+ if (found)
+ {
+ end_index = glyphs->log_clusters[i];
+ end_xpos = width;
+ end_glyph_pos = i;
+ break;
+ }
+ else
+ {
+ start_index = glyphs->log_clusters[i];
+ start_xpos = width;
+ start_glyph_pos = i;
+ }
+ }
- if (width <= x_pos && x_pos < width + glyphs->glyphs[i].geometry.width)
- found = TRUE;
+ if (width <= x_pos && x_pos < width + glyphs->glyphs[i].geometry.width)
+ found = TRUE;
- width += glyphs->glyphs[i].geometry.width;
- }
+ width += glyphs->glyphs[i].geometry.width;
+ }
}
if (end_index == -1)
@@ -727,70 +777,142 @@ pango_glyph_string_x_to_index (PangoGlyphString *glyphs,
end_xpos = (analysis->level % 2) ? 0 : width;
}
- /* Calculate number of chars within cluster */
- p = text + start_index;
- while (p < text + end_index)
+ if (start_xpos == end_xpos)
{
- p = g_utf8_next_char (p);
+ if (index)
+ *index = start_index;
+ if (trailing)
+ *trailing = FALSE;
+ return;
+ }
+
+ /* at this point:
+ * cluster goes from start_index to end_index in characters
+ * from start_glyph_pos to end_glyph_pos in glyphs
+ * from start_xpos to end_xpos in coordinates
+ */
+
+ /* Calculate number of chars within cluster
+ * To come up with accurate answers here, we need to know grapheme
+ * boundaries.
+ */
+ start_offset = attrs ? g_utf8_pointer_to_offset (text, text + start_index) : 0;
+ for (p = text + start_index, i = start_offset;
+ p < text + end_index;
+ p = g_utf8_next_char (p), i++)
+ {
+ if (attrs && !attrs[i].is_cursor_position)
+ continue;
+
cluster_chars++;
}
- if (start_xpos == end_xpos)
+ if (G_UNLIKELY (!cluster_chars))
{
if (index)
- *index = start_index;
+ *index = start_index;
if (trailing)
- *trailing = FALSE;
+ *trailing = FALSE;
+ return;
}
- else
+
+ if (cluster_chars > 1)
{
- double cp = ((double)(x_pos - start_xpos) * cluster_chars) / (end_xpos - start_xpos);
-
- /* LTR and right-to-left have to be handled separately
- * here because of the edge condition when we are exactly
- * at a pixel boundary; end_xpos goes with the next
- * character for LTR, with the previous character for RTL.
- */
- if (start_xpos < end_xpos) /* Left-to-right */
- {
- if (index)
- {
- const char *p = text + start_index;
- int i = 0;
+ hb_font_t *hb_font = pango_font_get_hb_font (analysis->font);
+ int glyph_pos = -1;
- while (i + 1 <= cp)
- {
- p = g_utf8_next_char (p);
- i++;
- }
+ if (start_glyph_pos == end_glyph_pos)
+ glyph_pos = start_glyph_pos;
+ else
+ {
+ hb_face_t *hb_face = hb_font_get_face (hb_font);
- *index = (p - text);
- }
+ for (i = start_glyph_pos; i <= end_glyph_pos; i++)
+ {
+ if (hb_ot_layout_get_glyph_class (hb_face, glyphs->glyphs[i].glyph) != HB_OT_LAYOUT_GLYPH_CLASS_MARK)
+ {
+ if (glyph_pos != -1)
+ {
+ /* multiple non-mark glyphs in cluster, giving up */
+ goto fallback;
+ }
+ glyph_pos = i;
+ }
+ }
+ if (glyph_pos == -1)
+ {
+ /* no non-mark glyph in a multi-glyph cluster, giving up */
+ goto fallback;
+ }
+ }
- if (trailing)
- *trailing = (cp - (int)cp >= 0.5) ? TRUE : FALSE;
- }
- else /* Right-to-left */
- {
- if (index)
- {
- const char *p = text + start_index;
- int i = 0;
+ hb_ot_layout_get_ligature_carets (hb_font,
+ (analysis->level % 2) ? HB_DIRECTION_RTL : HB_DIRECTION_LTR,
+ glyphs->glyphs[glyph_pos].glyph,
+ 0, &caret_count, caret);
- while (i + 1 < cp)
- {
- p = g_utf8_next_char (p);
- i++;
- }
+ if (caret_count == cluster_chars)
+ goto pick;
+ }
- *index = (p - text);
- }
+fallback:
+ for (i = 0; i <= cluster_chars; i++)
+ caret[i] = start_xpos + i * (end_xpos - start_xpos) / cluster_chars;
+
+pick:
+ /* LTR and right-to-left have to be handled separately
+ * here because of the edge condition when we are exactly
+ * at a pixel boundary; end_xpos goes with the next
+ * character for LTR, with the previous character for RTL.
+ */
+ if (start_xpos < end_xpos) /* Left-to-right */
+ {
+ if (index)
+ {
+ const char *p = text + start_index;
+ int i = 0;
- if (trailing)
- {
- double cp_flip = cluster_chars - cp;
- *trailing = (cp_flip - (int)cp_flip >= 0.5) ? FALSE : TRUE;
- }
- }
+ while (x_pos > caret[i])
+ {
+ p = g_utf8_next_char (p);
+ if (!attrs || attrs[start_offset + i].is_cursor_position)
+ i++;
+ }
+
+ *index = (p - text);
+ }
+
+ if (trailing)
+ {
+ if (i == 0)
+ *trailing = FALSE;
+ else
+ *trailing = caret[i] - x_pos < x_pos - caret[i - 1];
+ }
+ }
+ else /* Right-to-left */
+ {
+ if (index)
+ {
+ const char *p = text + start_index;
+ int i = 0;
+
+ while (x_pos > caret[i])
+ {
+ p = g_utf8_next_char (p);
+ if (!attrs || attrs[start_offset + i].is_cursor_position)
+ i++;
+ }
+
+ *index = (p - text);
+ }
+
+ if (trailing)
+ {
+ if (i == 0)
+ *trailing = TRUE;
+ else
+ *trailing = caret[i] - x_pos > x_pos - caret[i - 1];
+ }
}
}
diff --git a/pango/pango-glyph.h b/pango/pango-glyph.h
index cd29b77a..d85456f1 100644
--- a/pango/pango-glyph.h
+++ b/pango/pango-glyph.h
@@ -190,6 +190,17 @@ void pango_glyph_string_index_to_x (PangoGlyphStrin
int index_,
gboolean trailing,
int *x_pos);
+
+PANGO_AVAILABLE_IN_1_50
+void pango_glyph_string_index_to_x_full (PangoGlyphString *glyphs,
+ const char *text,
+ int length,
+ PangoAnalysis *analysis,
+ PangoLogAttr *attrs,
+ int index_,
+ gboolean trailing,
+ int *x_pos);
+
PANGO_AVAILABLE_IN_ALL
void pango_glyph_string_x_to_index (PangoGlyphString *glyphs,
const char *text,
@@ -200,14 +211,14 @@ void pango_glyph_string_x_to_index (PangoGlyphStrin
int *trailing);
PANGO_AVAILABLE_IN_1_50
-void pango_glyph_string_index_to_x_full (PangoGlyphString *glyphs,
+void pango_glyph_string_x_to_index_full (PangoGlyphString *glyphs,
const char *text,
int length,
PangoAnalysis *analysis,
PangoLogAttr *attrs,
- int index_,
- gboolean trailing,
- int *x_pos);
+ int x_pos,
+ int *index_,
+ int *trailing);
/* Shaping */