[pango/x-to-index-full: 1/2] Add pango_glyph_string_x_to_index_full




commit a89d6ab2716e1801647f3807c13fe5b9e20c21dc
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Aug 28 14:52:23 2021 -0400

    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.

 pango/glyphstring.c | 300 ++++++++++++++++++++++++++++++++++++----------------
 pango/pango-glyph.h |  19 +++-
 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
@@ -649,6 +649,44 @@ pango_glyph_string_x_to_index (PangoGlyphString *glyphs,
                                int               x_pos,
                                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;
@@ -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 */
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]