[pango/baseline-shift: 3/5] Implement font-dependent scaling




commit d40a7a8a7177d0cfb794600b83f7f39592beff31
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Aug 29 15:53:48 2021 -0400

    Implement font-dependent scaling
    
    Add a new font-scale attribute to indicate font size
    changes due to super- and subscript shifts, and handle
    it during item post-processing to find the right font
    sizes.

 pango/itemize.c          | 181 +++++++++++++++++++++++++++++++++++++++++++----
 pango/pango-attributes.c |  31 ++++++--
 pango/pango-attributes.h |  12 +++-
 pango/pango-layout.c     |   1 +
 pango/pango-markup.c     |  12 ++++
 tests/test-itemize.c     |   1 +
 6 files changed, 217 insertions(+), 21 deletions(-)
---
diff --git a/pango/itemize.c b/pango/itemize.c
index 11bc2513..29a1cdff 100644
--- a/pango/itemize.c
+++ b/pango/itemize.c
@@ -34,6 +34,7 @@
 #include "pango-attributes-private.h"
 #include "pango-item-private.h"
 
+#include <hb-ot.h>
 
 /* {{{ Font cache */
 
@@ -1019,6 +1020,169 @@ itemize_state_finish (ItemizeState *state)
   if (state->base_font)
     g_object_unref (state->base_font);
 }
+
+/* }}} */
+/* {{{ Post-processing */
+
+typedef struct {
+  PangoAttribute *attr;
+  double scale;
+} ScaleItem;
+
+static gboolean
+collect_font_scale (PangoContext  *context,
+                    GList        **stack,
+                    PangoItem     *item,
+                    PangoItem     *prev,
+                    double        *scale)
+{
+  gboolean retval = FALSE;
+  GList *l;
+
+  for (GSList *l = item->analysis.extra_attrs; l; l = l->next)
+    {
+      PangoAttribute *attr = l->data;
+
+      if (attr->klass->type == PANGO_ATTR_FONT_SCALE)
+        {
+          if (attr->start_index == item->offset)
+            {
+              ScaleItem *entry;
+              hb_font_t *hb_font;
+              int y_scale;
+              hb_position_t y_size;
+
+              entry = g_new (ScaleItem, 1);
+              entry->attr = attr;
+              *stack = g_list_prepend (*stack, entry);
+
+              hb_font = pango_font_get_hb_font (prev->analysis.font);
+              hb_font_get_scale (hb_font, NULL, &y_scale);
+
+              switch (((PangoAttrInt *)attr)->value)
+                {
+                case PANGO_FONT_SCALE_NONE:
+                  break;
+                case PANGO_FONT_SCALE_SUPERSCRIPT:
+                  if (hb_ot_metrics_get_position (hb_font,
+                                                  HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE,
+                                                  &y_size))
+                    entry->scale = y_size / (double) y_scale;
+                  else
+                    entry->scale = 1 / 1.2;
+                  break;
+                case PANGO_FONT_SCALE_SUBSCRIPT:
+                  if (hb_ot_metrics_get_position (hb_font,
+                                                  HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE,
+                                                  &y_size))
+                    entry->scale = y_size / (double) y_scale;
+                  else
+                    entry->scale = 1 / 1.2;
+                  break;
+                default:
+                  g_assert_not_reached ();
+                }
+            }
+        }
+     }
+
+   *scale = 1.0;
+
+   for (l = *stack; l; l = l->next)
+     {
+       ScaleItem *entry = l->data;
+       *scale *= entry->scale;
+       retval = TRUE;
+     }
+
+   l = *stack;
+   while (l)
+     {
+       ScaleItem *entry = l->data;
+       GList *next = l->next;
+
+       if (entry->attr->end_index == item->offset + item->length)
+         {
+           *stack = g_list_delete_link (*stack, l);
+           g_free (entry);
+         }
+
+       l = next;
+    }
+
+  return retval;
+}
+
+static void
+apply_scale_to_item (PangoContext *context,
+                     PangoItem    *item,
+                     double        scale)
+{
+  PangoFontDescription *desc;
+  double size;
+
+  desc = pango_font_describe (item->analysis.font);
+  size = scale * pango_font_description_get_size (desc);
+
+  if (pango_font_description_get_size_is_absolute (desc))
+    pango_font_description_set_absolute_size (desc, size);
+  else
+    pango_font_description_set_size (desc, size);
+
+  g_object_unref (item->analysis.font);
+  item->analysis.font = pango_font_map_load_font (context->font_map, context, desc);
+
+  pango_font_description_free (desc);
+}
+
+static void
+apply_font_scale (PangoContext *context,
+                  GList        *items)
+{
+  PangoItem *prev;
+  GList *stack = NULL;
+
+  for (GList *l = items; l; l = l->next)
+    {
+      PangoItem *item = l->data;
+      double scale;
+
+      if (collect_font_scale (context, &stack, item, prev, &scale))
+        apply_scale_to_item (context, item, scale);
+
+      prev = item;
+    }
+
+  if (stack != NULL)
+    {
+      g_warning ("Leftover font scales");
+      g_list_free_full (stack, g_free);
+    }
+}
+
+static GList *
+post_process_items (PangoContext *context,
+                    GList        *items)
+{
+  items = g_list_reverse (items);
+
+  /* Compute the char offset for each item */
+  {
+    int char_offset = 0;
+    for (GList *l = items; l; l = l->next)
+      {
+        PangoItemPrivate *item = l->data;
+        item->char_offset = char_offset;
+        char_offset += item->num_chars;
+      }
+  }
+
+  /* apply font-scale */
+  apply_font_scale (context, items);
+
+  return items;
+}
+
 /* }}} */
 /* {{{ Public API */
 
@@ -1034,8 +1198,6 @@ pango_itemize_with_font (PangoContext               *context,
                          const PangoFontDescription *desc)
 {
   ItemizeState state;
-  GList *items;
-  int char_offset;
 
   if (length == 0 || g_utf8_get_char (text + start_index) == '\0')
     return NULL;
@@ -1049,18 +1211,7 @@ pango_itemize_with_font (PangoContext               *context,
 
   itemize_state_finish (&state);
 
-  items = g_list_reverse (state.result);
-
-  /* Compute the char offset for each item */
-  char_offset = 0;
-  for (GList *l = items; l; l = l->next)
-    {
-      PangoItemPrivate *item = l->data;
-      item->char_offset = char_offset;
-      char_offset += item->num_chars;
-    }
-
-  return items;
+  return post_process_items (context, state.result);
 }
 
 /**
@@ -1154,6 +1305,6 @@ pango_itemize (PangoContext      *context,
                                   NULL);
 }
 
-/* }}} */
+ /* }}} */
 
 /* vim:set foldmethod=marker expandtab: */
diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c
index 8507c963..65af8f3b 100644
--- a/pango/pango-attributes.c
+++ b/pango/pango-attributes.c
@@ -947,6 +947,20 @@ pango_attr_baseline_shift_new (int rise)
 
   return pango_attr_int_new (&klass, (int)rise);
 }
+
+PangoAttribute *
+pango_attr_font_scale_new (PangoFontScale scale)
+{
+  static const PangoAttrClass klass = {
+    PANGO_ATTR_FONT_SCALE,
+    pango_attr_int_copy,
+    pango_attr_int_destroy,
+    pango_attr_int_equal
+  };
+
+  return pango_attr_int_new (&klass, (int)scale);
+}
+
 /**
  * pango_attr_scale_new:
  * @scale_factor: factor to scale the font
@@ -1558,6 +1572,7 @@ pango_attribute_as_int (PangoAttribute *attr)
     case PANGO_ATTR_WORD:
     case PANGO_ATTR_SENTENCE:
     case PANGO_ATTR_BASELINE_SHIFT:
+    case PANGO_ATTR_FONT_SCALE:
       return (PangoAttrInt *)attr;
 
     default:
@@ -2847,10 +2862,14 @@ pango_attr_iterator_get_font (PangoAttrIterator     *iterator,
             {
               gboolean found = FALSE;
 
-              /* Hack: special-case FONT_FEATURES.  We don't want them to
-               * override each other, so we never merge them.  This should
-               * be fixed when we implement attr-merging. */
-              if (attr->klass->type != PANGO_ATTR_FONT_FEATURES)
+              /* Hack: special-case FONT_FEATURES, BASELINE_SHIFT and FONT_SCALE.
+               * We don't want these to accumulate, not override each other,
+               * so we never merge them.
+               * This needs to be handled more systematically.
+               */
+              if (attr->klass->type != PANGO_ATTR_FONT_FEATURES &&
+                  attr->klass->type != PANGO_ATTR_BASELINE_SHIFT &&
+                  attr->klass->type != PANGO_ATTR_FONT_SCALE)
                 {
                   GSList *tmp_list = *extra_attrs;
                   while (tmp_list)
@@ -2917,7 +2936,9 @@ pango_attr_iterator_get_attrs (PangoAttrIterator *iterator)
       GSList *tmp_list2;
       gboolean found = FALSE;
 
-      if (attr->klass->type != PANGO_ATTR_FONT_DESC)
+      if (attr->klass->type != PANGO_ATTR_FONT_DESC &&
+          attr->klass->type != PANGO_ATTR_BASELINE_SHIFT &&
+          attr->klass->type != PANGO_ATTR_FONT_SCALE)
         for (tmp_list2 = attrs; tmp_list2; tmp_list2 = tmp_list2->next)
           {
             PangoAttribute *old_attr = tmp_list2->data;
diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h
index 1c9df2c5..59183a60 100644
--- a/pango/pango-attributes.h
+++ b/pango/pango-attributes.h
@@ -79,7 +79,8 @@ typedef struct _PangoAttrFontFeatures PangoAttrFontFeatures;
  * @PANGO_ATTR_ABSOLUTE_LINE_HEIGHT: line height ([struct@Pango.AttrInt]). Since: 1.50
  * @PANGO_ATTR_WORD: override segmentation to classify the range of the attribute as a single word 
([struct@Pango.AttrInt]). Since 1.50
  * @PANGO_ATTR_SENTENCE: override segmentation to classify the range of the attribute as a single sentence 
([struct@Pango.AttrInt]). Since 1.50
- * @PANGO_ATTR_BASELINE_SHIFT: baseline displacement ([struct@Pango.AttrSize]). Since 1.50
+ * @PANGO_ATTR_BASELINE_SHIFT: baseline displacement ([struct@Pango.AttrInt]). Since 1.50
+ * @PANGO_ATTR_FONT_SCALE: font-relative size change ([struct@Pango.AttrInt]). Since 1.50
  *
  * The `PangoAttrType` distinguishes between different types of attributes.
  *
@@ -127,6 +128,7 @@ typedef enum
   PANGO_ATTR_WORD,              /* PangoAttrInt */
   PANGO_ATTR_SENTENCE,          /* PangoAttrInt */
   PANGO_ATTR_BASELINE_SHIFT,    /* PangoAttrSize */
+  PANGO_ATTR_FONT_SCALE,        /* PangoAttrInt */
 } PangoAttrType;
 
 /**
@@ -245,6 +247,12 @@ typedef enum {
   PANGO_BASELINE_SHIFT_SUBSCRIPT,
 } PangoBaselineShift;
 
+typedef enum {
+  PANGO_FONT_SCALE_NONE,
+  PANGO_FONT_SCALE_SUPERSCRIPT,
+  PANGO_FONT_SCALE_SUBSCRIPT,
+} PangoFontScale;
+
 /**
  * PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING:
  *
@@ -536,6 +544,8 @@ PANGO_AVAILABLE_IN_ALL
 PangoAttribute *        pango_attr_rise_new                     (int                         rise);
 PANGO_AVAILABLE_IN_1_50
 PangoAttribute *        pango_attr_baseline_shift_new           (int                         shift);
+PANGO_AVAILABLE_IN_1_50
+PangoAttribute *        pango_attr_font_scale_new               (PangoFontScale              scale);
 PANGO_AVAILABLE_IN_ALL
 PangoAttribute *        pango_attr_scale_new                    (double                      scale_factor);
 PANGO_AVAILABLE_IN_1_4
diff --git a/pango/pango-layout.c b/pango/pango-layout.c
index 58e3a91c..6c1e2e5e 100644
--- a/pango/pango-layout.c
+++ b/pango/pango-layout.c
@@ -4321,6 +4321,7 @@ affects_itemization (PangoAttribute *attr,
     case PANGO_ATTR_ABSOLUTE_SIZE:
     case PANGO_ATTR_GRAVITY:
     case PANGO_ATTR_GRAVITY_HINT:
+    case PANGO_ATTR_FONT_SCALE:
     /* These need to be constant across runs */
     case PANGO_ATTR_LETTER_SPACING:
     case PANGO_ATTR_SHAPE:
diff --git a/pango/pango-markup.c b/pango/pango-markup.c
index 65396547..54c08c67 100644
--- a/pango/pango-markup.c
+++ b/pango/pango-markup.c
@@ -1232,6 +1232,7 @@ span_parse_func     (MarkupData            *md G_GNUC_UNUSED,
   const char *line_height = NULL;
   const char *text_transform = NULL;
   const char *segment = NULL;
+  const char *font_scale = NULL;
 
   g_markup_parse_context_get_position (context,
                                       &line_number, &char_number);
@@ -1286,6 +1287,7 @@ span_parse_func     (MarkupData            *md G_GNUC_UNUSED,
        CHECK_ATTRIBUTE2(style, "font_style");
        CHECK_ATTRIBUTE2(variant, "font_variant");
        CHECK_ATTRIBUTE2(weight, "font_weight");
+       CHECK_ATTRIBUTE(font_scale);
 
        CHECK_ATTRIBUTE (foreground);
        CHECK_ATTRIBUTE2(foreground, "fgcolor");
@@ -1699,6 +1701,16 @@ span_parse_func     (MarkupData            *md G_GNUC_UNUSED,
 
     }
 
+  if (G_UNLIKELY (font_scale))
+    {
+      PangoFontScale scale;
+
+      if (!span_parse_enum ("font_scale", font_scale, PANGO_TYPE_FONT_SCALE, (int*)(void*)&scale, 
line_number, error))
+       goto error;
+
+      add_attribute (tag, pango_attr_font_scale_new (scale));
+    }
+
   if (G_UNLIKELY (letter_spacing))
     {
       gint n = 0;
diff --git a/tests/test-itemize.c b/tests/test-itemize.c
index 29f59210..e5775985 100644
--- a/tests/test-itemize.c
+++ b/tests/test-itemize.c
@@ -73,6 +73,7 @@ affects_itemization (PangoAttribute *attr,
     case PANGO_ATTR_ABSOLUTE_SIZE:
     case PANGO_ATTR_GRAVITY:
     case PANGO_ATTR_GRAVITY_HINT:
+    case PANGO_ATTR_FONT_SCALE:
     /* These are part of ItemProperties, so need to break runs */
     case PANGO_ATTR_LETTER_SPACING:
     case PANGO_ATTR_SHAPE:


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