summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2021-08-29 15:53:48 -0400
committerMatthias Clasen <mclasen@redhat.com>2021-08-31 14:29:56 -0400
commitfd51b46898ac9c880bb9552c3e301bfef7ac22bd (patch)
tree5afaca48cf1ecdcac7d4e91c7db80ddb484c5267
parent303b4fb73eb8848d18abbda4d151461bd86f1000 (diff)
downloadpango-fd51b46898ac9c880bb9552c3e301bfef7ac22bd.tar.gz
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.
-rw-r--r--pango/itemize.c181
-rw-r--r--pango/pango-attributes.c31
-rw-r--r--pango/pango-attributes.h12
-rw-r--r--pango/pango-layout.c1
-rw-r--r--pango/pango-markup.c12
-rw-r--r--tests/test-itemize.c1
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: