summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2021-11-08 13:47:52 +0000
committerMatthias Clasen <mclasen@redhat.com>2021-11-08 13:47:52 +0000
commit78d4efd6af8fc37b93b1dafb7c3b7101b389a565 (patch)
tree857deeac1c70f10038c2e967d0238f1845964533
parent9d389e936707ebcf84fbc64d07b8aa17cdf2ef04 (diff)
parent1790dbf9da8a4249528833ead1af32d232861b64 (diff)
downloadpango-78d4efd6af8fc37b93b1dafb7c3b7101b389a565.tar.gz
Merge branch 'small-caps' into 'main'
Emulate Small Caps if not available from the font See merge request GNOME/pango!497
-rw-r--r--pango/fonts.c7
-rw-r--r--pango/itemize.c482
-rw-r--r--pango/pango-attributes.h4
-rw-r--r--pango/pango-context-private.h10
-rw-r--r--pango/pango-context.c1
-rw-r--r--pango/pango-font.h26
-rw-r--r--pango/pango-item-private.h21
-rw-r--r--pango/pango-item.c68
-rw-r--r--pango/pango-layout.c20
-rw-r--r--pango/pangofc-fontmap.c71
-rw-r--r--pango/shape.c75
-rw-r--r--tests/test-font.c46
12 files changed, 661 insertions, 170 deletions
diff --git a/pango/fonts.c b/pango/fonts.c
index fd98d91e..70a987e6 100644
--- a/pango/fonts.c
+++ b/pango/fonts.c
@@ -1019,7 +1019,12 @@ static const FieldMap style_map[] = {
static const FieldMap variant_map[] = {
{ PANGO_VARIANT_NORMAL, "" },
- { PANGO_VARIANT_SMALL_CAPS, "Small-Caps" }
+ { PANGO_VARIANT_SMALL_CAPS, "Small-Caps" },
+ { PANGO_VARIANT_ALL_SMALL_CAPS, "All-Small-Caps" },
+ { PANGO_VARIANT_PETITE_CAPS, "Petite-Caps" },
+ { PANGO_VARIANT_ALL_PETITE_CAPS, "All-Petite-Caps" },
+ { PANGO_VARIANT_UNICASE, "Unicase" },
+ { PANGO_VARIANT_TITLE_CAPS, "Title-Caps" }
};
static const FieldMap weight_map[] = {
diff --git a/pango/itemize.c b/pango/itemize.c
index bd339633..82c737cc 100644
--- a/pango/itemize.c
+++ b/pango/itemize.c
@@ -902,6 +902,36 @@ itemize_state_update_for_new_run (ItemizeState *state)
}
}
+/* We don't want space characters to affect font selection; in general,
+* it's always wrong to select a font just to render a space.
+* We assume that all fonts have the ASCII space, and for other space
+* characters if they don't, HarfBuzz will compatibility-decompose them
+* to ASCII space...
+* See bugs #355987 and #701652.
+*
+* We don't want to change fonts just for variation selectors.
+* See bug #781123.
+*
+* Finally, don't change fonts for line or paragraph separators.
+*
+* Note that we want spaces to use the 'better' font, comparing
+* the font that is used before and after the space. This is handled
+* in itemize_state_add_character().
+*/
+static gboolean
+consider_as_space (gunichar wc)
+{
+ GUnicodeType type = g_unichar_type (wc);
+ return type == G_UNICODE_CONTROL ||
+ type == G_UNICODE_FORMAT ||
+ type == G_UNICODE_SURROGATE ||
+ type == G_UNICODE_LINE_SEPARATOR ||
+ type == G_UNICODE_PARAGRAPH_SEPARATOR ||
+ (type == G_UNICODE_SPACE_SEPARATOR && wc != 0x1680u /* OGHAM SPACE MARK */) ||
+ (wc >= 0xfe00u && wc <= 0xfe0fu) ||
+ (wc >= 0xe0100u && wc <= 0xe01efu);
+}
+
static void
itemize_state_process_run (ItemizeState *state)
{
@@ -926,33 +956,8 @@ itemize_state_process_run (ItemizeState *state)
gboolean is_forced_break = (wc == '\t' || wc == LINE_SEPARATOR);
PangoFont *font;
int font_position;
- GUnicodeType type;
-
- /* We don't want space characters to affect font selection; in general,
- * it's always wrong to select a font just to render a space.
- * We assume that all fonts have the ASCII space, and for other space
- * characters if they don't, HarfBuzz will compatibility-decompose them
- * to ASCII space...
- * See bugs #355987 and #701652.
- *
- * We don't want to change fonts just for variation selectors.
- * See bug #781123.
- *
- * Finally, don't change fonts for line or paragraph separators.
- *
- * Note that we want spaces to use the 'better' font, comparing
- * the font that is used before and after the space. This is handled
- * in itemize_state_add_character().
- */
- type = g_unichar_type (wc);
- if (G_UNLIKELY (type == G_UNICODE_CONTROL ||
- type == G_UNICODE_FORMAT ||
- type == G_UNICODE_SURROGATE ||
- type == G_UNICODE_LINE_SEPARATOR ||
- type == G_UNICODE_PARAGRAPH_SEPARATOR ||
- (type == G_UNICODE_SPACE_SEPARATOR && wc != 0x1680u /* OGHAM SPACE MARK */) ||
- (wc >= 0xfe00u && wc <= 0xfe0fu) ||
- (wc >= 0xe0100u && wc <= 0xe01efu)))
+
+ if (consider_as_space (wc))
{
font = NULL;
font_position = 0xffff;
@@ -1024,6 +1029,8 @@ itemize_state_finish (ItemizeState *state)
/* }}} */
/* {{{ Post-processing */
+ /* {{{ Handling font scale */
+
typedef struct {
PangoAttribute *attr;
double scale;
@@ -1048,43 +1055,61 @@ collect_font_scale (PangoContext *context,
if (attr->start_index == item->offset)
{
ScaleItem *entry;
- hb_font_t *hb_font;
int y_scale;
hb_position_t y_size;
+ hb_position_t cap_height;
+ hb_position_t x_height;
entry = g_new (ScaleItem, 1);
entry->attr = attr;
*stack = g_list_prepend (*stack, entry);
- if (prev)
- {
- hb_font = pango_font_get_hb_font (prev->analysis.font);
- hb_font_get_scale (hb_font, NULL, &y_scale);
- }
- else
- hb_font = NULL;
-
switch (((PangoAttrInt *)attr)->value)
{
case PANGO_FONT_SCALE_NONE:
break;
case PANGO_FONT_SCALE_SUPERSCRIPT:
- if (hb_font &&
- hb_ot_metrics_get_position (hb_font,
+ if (prev &&
+ hb_ot_metrics_get_position (pango_font_get_hb_font (prev->analysis.font),
HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE,
&y_size))
- entry->scale = y_size / (double) y_scale;
+ {
+ hb_font_get_scale (pango_font_get_hb_font (prev->analysis.font), NULL, &y_scale);
+ entry->scale = y_size / (double) y_scale;
+ }
else
- entry->scale = 1 / 1.2;
+ {
+ entry->scale = 1 / 1.2;
+ }
break;
case PANGO_FONT_SCALE_SUBSCRIPT:
- if (hb_font &&
- hb_ot_metrics_get_position (hb_font,
+ if (prev &&
+ hb_ot_metrics_get_position (pango_font_get_hb_font (prev->analysis.font),
HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE,
&y_size))
- entry->scale = y_size / (double) y_scale;
+ {
+ hb_font_get_scale (pango_font_get_hb_font (prev->analysis.font), NULL, &y_scale);
+ entry->scale = y_size / (double) y_scale;
+ }
else
- entry->scale = 1 / 1.2;
+ {
+ entry->scale = 1 / 1.2;
+ }
+ break;
+ case PANGO_FONT_SCALE_SMALL_CAPS:
+ if (hb_ot_metrics_get_position (pango_font_get_hb_font (item->analysis.font),
+ HB_OT_METRICS_TAG_CAP_HEIGHT,
+ &cap_height) &&
+ hb_ot_metrics_get_position (pango_font_get_hb_font (item->analysis.font),
+ HB_OT_METRICS_TAG_X_HEIGHT,
+ &x_height))
+ {
+ entry->scale = x_height / (double) cap_height;
+ }
+ else
+ {
+ entry->scale = 0.8;
+ }
break;
default:
g_assert_not_reached ();
@@ -1167,33 +1192,333 @@ apply_font_scale (PangoContext *context,
}
}
+/* }}} */
+/* {{{ Handling Casing variants */
+
+static gboolean
+all_features_supported (PangoItem *item,
+ hb_tag_t *features,
+ guint n_features)
+{
+ hb_font_t *font = pango_font_get_hb_font (item->analysis.font);
+ hb_face_t *face = hb_font_get_face (font);
+ hb_script_t script;
+ hb_language_t language;
+ guint script_count = HB_OT_MAX_TAGS_PER_SCRIPT;
+ hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT];
+ hb_tag_t chosen_script;
+ guint language_count = HB_OT_MAX_TAGS_PER_LANGUAGE;
+ hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE];
+ guint script_index, language_index;
+ guint index;
+
+ script = g_unicode_script_to_iso15924 (item->analysis.script);
+ language = hb_language_from_string (pango_language_to_string (item->analysis.language), -1);
+
+ hb_ot_tags_from_script_and_language (script, language,
+ &script_count, script_tags,
+ &language_count, language_tags);
+ hb_ot_layout_table_select_script (face, HB_OT_TAG_GSUB,
+ script_count, script_tags,
+ &script_index,
+ &chosen_script);
+ hb_ot_layout_script_select_language (face, HB_OT_TAG_GSUB,
+ script_index,
+ language_count, language_tags,
+ &language_index);
+
+ for (int i = 0; i < n_features; i++)
+ {
+ if (!hb_ot_layout_language_find_feature (face, HB_OT_TAG_GSUB,
+ script_index, language_index,
+ features[i],
+ &index))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+variant_supported (PangoItem *item,
+ PangoVariant variant)
+{
+ hb_tag_t features[2];
+ guint num_features = 0;
+
+ switch (variant)
+ {
+ case PANGO_VARIANT_NORMAL:
+ case PANGO_VARIANT_TITLE_CAPS:
+ return TRUE;
+ case PANGO_VARIANT_SMALL_CAPS:
+ features[num_features++] = HB_TAG ('s', 'm', 'c', 'p');
+ break;
+ case PANGO_VARIANT_ALL_SMALL_CAPS:
+ features[num_features++] = HB_TAG ('s', 'm', 'c', 'p');
+ features[num_features++] = HB_TAG ('c', '2', 's', 'c');
+ break;
+ case PANGO_VARIANT_PETITE_CAPS:
+ features[num_features++] = HB_TAG ('p', 'c', 'a', 'p');
+ break;
+ case PANGO_VARIANT_ALL_PETITE_CAPS:
+ features[num_features++] = HB_TAG ('p', 'c', 'a', 'p');
+ features[num_features++] = HB_TAG ('c', '2', 'p', 'c');
+ break;
+ case PANGO_VARIANT_UNICASE:
+ features[num_features++] = HB_TAG ('u', 'n', 'i', 'c');
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ return all_features_supported (item, features, num_features);
+}
+
+static PangoVariant
+get_font_variant (PangoItem *item)
+{
+ PangoFontDescription *desc;
+ PangoVariant variant;
+
+ desc = pango_font_describe (item->analysis.font);
+ variant = pango_font_description_get_variant (desc);
+ pango_font_description_free (desc);
+
+ return variant;
+}
+
+static PangoTextTransform
+find_text_transform (const PangoAnalysis *analysis)
+{
+ GSList *l;
+ PangoTextTransform transform = PANGO_TEXT_TRANSFORM_NONE;
+
+ for (l = analysis->extra_attrs; l; l = l->next)
+ {
+ PangoAttribute *attr = l->data;
+
+ if (attr->klass->type == PANGO_ATTR_TEXT_TRANSFORM)
+ transform = (PangoTextTransform) ((PangoAttrInt*)attr)->value;
+ }
+
+ return transform;
+}
+
+/* Split list_item into upper- and lowercase runs, and
+ * add font scale and text transform attributes to make
+ * them be appear according to variant. The log_attrs are
+ * needed for taking text transforms into account when
+ * determining the case of characters int he run.
+ */
+static void
+split_item_for_variant (const char *text,
+ PangoLogAttr *log_attrs,
+ PangoVariant variant,
+ GList *list_item)
+{
+ PangoItem *item = list_item->data;
+ const char *start, *end;
+ const char *p, *p0;
+ gunichar wc;
+ PangoTextTransform transform = PANGO_TEXT_TRANSFORM_NONE;
+ PangoFontScale lowercase_scale = PANGO_FONT_SCALE_NONE;
+ PangoFontScale uppercase_scale = PANGO_FONT_SCALE_NONE;
+ PangoTextTransform item_transform;
+ gboolean is_word_start;
+ int offset;
+
+ switch (variant)
+ {
+ case PANGO_VARIANT_ALL_SMALL_CAPS:
+ case PANGO_VARIANT_ALL_PETITE_CAPS:
+ uppercase_scale = PANGO_FONT_SCALE_SMALL_CAPS;
+ G_GNUC_FALLTHROUGH;
+ case PANGO_VARIANT_SMALL_CAPS:
+ case PANGO_VARIANT_PETITE_CAPS:
+ transform = PANGO_TEXT_TRANSFORM_UPPERCASE;
+ lowercase_scale = PANGO_FONT_SCALE_SMALL_CAPS;
+ break;
+ case PANGO_VARIANT_UNICASE:
+ uppercase_scale = PANGO_FONT_SCALE_SMALL_CAPS;
+ break;
+ case PANGO_VARIANT_NORMAL:
+ case PANGO_VARIANT_TITLE_CAPS:
+ default:
+ g_assert_not_reached ();
+ }
+
+ item_transform = find_text_transform (&item->analysis);
+
+ start = text + item->offset;
+ end = start + item->length;
+ offset = ((PangoItemPrivate *)item)->char_offset;
+
+ p = start;
+ while (p < end)
+ {
+ p0 = p;
+ wc = g_utf8_get_char (p);
+ is_word_start = log_attrs && log_attrs[offset].is_word_start;
+ while (p < end && (item_transform == PANGO_TEXT_TRANSFORM_LOWERCASE ||
+ consider_as_space (wc) ||
+ (g_unichar_islower (wc) &&
+ !(item_transform == PANGO_TEXT_TRANSFORM_UPPERCASE ||
+ (item_transform == PANGO_TEXT_TRANSFORM_CAPITALIZE && is_word_start)))))
+ {
+ p = g_utf8_next_char (p);
+ wc = g_utf8_get_char (p);
+ offset++;
+ is_word_start = log_attrs && log_attrs[offset].is_word_start;
+ }
+
+ if (p0 < p)
+ {
+ PangoItem *new_item;
+ PangoAttribute *attr;
+
+ /* p0 .. p is a lowercase segment */
+ if (p < end)
+ {
+ new_item = pango_item_split (item, p - p0, g_utf8_strlen (p, p - p0));
+ list_item->data = new_item;
+ list_item = g_list_insert_before (list_item, list_item->next, item);
+ list_item = list_item->next;
+ }
+ else
+ {
+ new_item = item;
+ }
+
+ if (transform != PANGO_TEXT_TRANSFORM_NONE)
+ {
+ attr = pango_attr_text_transform_new (transform);
+ attr->start_index = new_item->offset;
+ attr->end_index = new_item->offset + new_item->length;
+ new_item->analysis.extra_attrs = g_slist_append (new_item->analysis.extra_attrs, attr);
+ }
+
+ if (lowercase_scale != PANGO_FONT_SCALE_NONE)
+ {
+ attr = pango_attr_font_scale_new (lowercase_scale);
+ attr->start_index = new_item->offset;
+ attr->end_index = new_item->offset + new_item->length;
+ new_item->analysis.extra_attrs = g_slist_append (new_item->analysis.extra_attrs, attr);
+ }
+ }
+
+ p0 = p;
+ wc = g_utf8_get_char (p);
+ is_word_start = log_attrs && log_attrs[offset].is_word_start;
+ while (p < end && (item_transform == PANGO_TEXT_TRANSFORM_UPPERCASE ||
+ consider_as_space (wc) ||
+ !(item_transform == PANGO_TEXT_TRANSFORM_LOWERCASE || g_unichar_islower (wc)) ||
+ (item_transform == PANGO_TEXT_TRANSFORM_CAPITALIZE && is_word_start)))
+ {
+ p = g_utf8_next_char (p);
+ wc = g_utf8_get_char (p);
+ offset++;
+ is_word_start = log_attrs && log_attrs[offset].is_word_start;
+ }
+
+ if (p0 < p)
+ {
+ PangoItem *new_item;
+ PangoAttribute *attr;
+
+ /* p0 .. p is a uppercase segment */
+ if (p < end)
+ {
+ new_item = pango_item_split (item, p - p0, g_utf8_strlen (p, p - p0));
+ list_item->data = new_item;
+ list_item = g_list_insert_before (list_item, list_item->next, item);
+ list_item = list_item->next;
+ }
+ else
+ {
+ new_item = item;
+ }
+
+ if (uppercase_scale != PANGO_FONT_SCALE_NONE)
+ {
+ attr = pango_attr_font_scale_new (uppercase_scale);
+ attr->start_index = new_item->offset;
+ attr->end_index = new_item->offset + new_item->length;
+ new_item->analysis.extra_attrs = g_slist_append (new_item->analysis.extra_attrs, attr);
+ }
+ }
+ }
+}
+
+static void
+handle_variants_for_item (const char *text,
+ PangoLogAttr *log_attrs,
+ GList *l)
+{
+ PangoItem *item = l->data;
+ PangoVariant variant;
+
+ variant = get_font_variant (item);
+ if (!variant_supported (item, variant))
+ split_item_for_variant (text, log_attrs, variant, l);
+}
+
+static void
+handle_variants (const char *text,
+ PangoLogAttr *log_attrs,
+ GList *items)
+{
+ GList *next;
+
+ for (GList *l = items; l; l = next)
+ {
+ next = l->next;
+ handle_variants_for_item (text, log_attrs, l);
+ }
+}
+
+/* }}} */
+
static GList *
-post_process_items (PangoContext *context,
- GList *items)
+reorder_items (PangoContext *context,
+ GList *items)
{
+ int char_offset = 0;
+
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 */
+ /* Also cmpute the char offset for each item here */
+ for (GList *l = items; l; l = l->next)
+ {
+ PangoItemPrivate *item = l->data;
+ item->char_offset = char_offset;
+ char_offset += item->num_chars;
+ }
+
+ return items;
+}
+
+static GList *
+post_process_items (PangoContext *context,
+ const char *text,
+ PangoLogAttr *log_attrs,
+ GList *items)
+{
+ handle_variants (text, log_attrs, items);
apply_font_scale (context, items);
return items;
}
/* }}} */
-/* {{{ Public API */
+/* {{{ Private API */
-/* Like pango_itemize_with_base_dir, but takes a font description */
+/* Like pango_itemize_with_base_dir, but takes a font description.
+ * In contrast to pango_itemize_with_base_dir, this function does
+ * not call pango_itemize_post_process_items, so you need to do that
+ * separately, after applying attributes that affect segmentation and
+ * computing the log attrs.
+ */
GList *
pango_itemize_with_font (PangoContext *context,
PangoDirection base_dir,
@@ -1218,9 +1543,23 @@ pango_itemize_with_font (PangoContext *context,
itemize_state_finish (&state);
- return post_process_items (context, state.result);
+ return reorder_items (context, state.result);
+}
+
+/* Apply post-processing steps that may require log attrs.
+ */
+GList *
+pango_itemize_post_process_items (PangoContext *context,
+ const char *text,
+ PangoLogAttr *log_attrs,
+ GList *items)
+{
+ return post_process_items (context, text, log_attrs, items);
}
+/* }}} */
+/* {{{ Public API */
+
/**
* pango_itemize_with_base_dir:
* @context: a structure holding information that affects
@@ -1254,15 +1593,19 @@ pango_itemize_with_base_dir (PangoContext *context,
PangoAttrList *attrs,
PangoAttrIterator *cached_iter)
{
+ GList *items;
+
g_return_val_if_fail (context != NULL, NULL);
g_return_val_if_fail (start_index >= 0, NULL);
g_return_val_if_fail (length >= 0, NULL);
g_return_val_if_fail (length == 0 || text != NULL, NULL);
- return pango_itemize_with_font (context, base_dir,
- text, start_index, length,
- attrs, cached_iter,
- NULL);
+ items = pango_itemize_with_font (context, base_dir,
+ text, start_index, length,
+ attrs, cached_iter,
+ NULL);
+
+ return pango_itemize_post_process_items (context, text, NULL, items);
}
/**
@@ -1306,12 +1649,11 @@ pango_itemize (PangoContext *context,
g_return_val_if_fail (length >= 0, NULL);
g_return_val_if_fail (length == 0 || text != NULL, NULL);
- return pango_itemize_with_font (context, context->base_dir,
- text, start_index, length,
- attrs, cached_iter,
- NULL);
+ return pango_itemize_with_base_dir (context, context->base_dir,
+ text, start_index, length,
+ attrs, cached_iter);
}
- /* }}} */
+ /* }}} */
/* vim:set foldmethod=marker expandtab: */
diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h
index 51b9b077..018417d5 100644
--- a/pango/pango-attributes.h
+++ b/pango/pango-attributes.h
@@ -253,9 +253,10 @@ typedef enum {
* @PANGO_FONT_SCALE_NONE: Leave the font size unchanged
* @PANGO_FONT_SCALE_SUPERSCRIPT: Change the font to a size suitable for superscripts
* @PANGO_FONT_SCALE_SUBSCRIPT: Change the font to a size suitable for subscripts
+ * @PANGO_FONT_SCALE_SMALL_CAPS: Change the font to a size suitable for Small Caps. Since: 1.50
*
* An enumeration that affects font sizes for superscript
- * and subscript positioning.
+ * and subscript positioning and for (emulated) Small Caps.
*
* Since: 1.50
*/
@@ -263,6 +264,7 @@ typedef enum {
PANGO_FONT_SCALE_NONE,
PANGO_FONT_SCALE_SUPERSCRIPT,
PANGO_FONT_SCALE_SUBSCRIPT,
+ PANGO_FONT_SCALE_SMALL_CAPS,
} PangoFontScale;
/**
diff --git a/pango/pango-context-private.h b/pango/pango-context-private.h
index d65406e1..71f43b60 100644
--- a/pango/pango-context-private.h
+++ b/pango/pango-context-private.h
@@ -50,16 +50,6 @@ struct _PangoContext
gboolean round_glyph_positions;
};
-GList * pango_itemize_with_font (PangoContext *context,
- PangoDirection base_dir,
- const char *text,
- int start_index,
- int length,
- PangoAttrList *attrs,
- PangoAttrIterator *cached_iter,
- const PangoFontDescription *desc);
-
-
G_END_DECLS
#endif /* __PANGO_CONTEXT_PRIVATE_H__ */
diff --git a/pango/pango-context.c b/pango/pango-context.c
index 2301138f..d0166d94 100644
--- a/pango/pango-context.c
+++ b/pango/pango-context.c
@@ -28,6 +28,7 @@
#include "pango-impl-utils.h"
#include "pango-font-private.h"
+#include "pango-item-private.h"
#include "pango-fontset-private.h"
#include "pango-fontmap-private.h"
#include "pango-script-private.h"
diff --git a/pango/pango-font.h b/pango/pango-font.h
index d4bded86..6f3284fa 100644
--- a/pango/pango-font.h
+++ b/pango/pango-font.h
@@ -81,13 +81,35 @@ typedef enum {
* PangoVariant:
* @PANGO_VARIANT_NORMAL: A normal font.
* @PANGO_VARIANT_SMALL_CAPS: A font with the lower case characters
- * replaced by smaller variants of the capital characters.
+ * replaced by smaller variants of the capital characters.
+ * @PANGO_VARIANT_ALL_SMALL_CAPS: A font with all characters
+ * replaced by smaller variants of the capital characters.
+ * Since: 1.50
+ * @PANGO_VARIANT_PETITE_CAPS: A font with the lower case characters
+ * replaced by smaller variants of the capital characters.
+ * Petite Caps can be even smaller than Small Caps.
+ * Since: 1.50
+ * @PANGO_VARIANT_ALL_PETITE_CAPS: A font with all characters
+ * replaced by smaller variants of the capital characters.
+ * Petite Caps can be even smaller than Small Caps.
+ * Since: 1.50
+ * @PANGO_VARIANT_UNICASE: A font with the upper case characters
+ * replaced by smaller variants of the capital letters.
+ * Since: 1.50
+ * @PANGO_VARIANT_TITLE_CAPS: A font with capital letters that
+ * are more suitable for all-uppercase titles.
+ * Since: 1.50
*
* An enumeration specifying capitalization variant of the font.
*/
typedef enum {
PANGO_VARIANT_NORMAL,
- PANGO_VARIANT_SMALL_CAPS
+ PANGO_VARIANT_SMALL_CAPS,
+ PANGO_VARIANT_ALL_SMALL_CAPS,
+ PANGO_VARIANT_PETITE_CAPS,
+ PANGO_VARIANT_ALL_PETITE_CAPS,
+ PANGO_VARIANT_UNICASE,
+ PANGO_VARIANT_TITLE_CAPS
} PangoVariant;
/**
diff --git a/pango/pango-item-private.h b/pango/pango-item-private.h
index 8680fa4b..ef3e8ed0 100644
--- a/pango/pango-item-private.h
+++ b/pango/pango-item-private.h
@@ -22,6 +22,7 @@
#define __PANGO_ITEM_PRIVATE_H__
#include <pango/pango-item.h>
+#include <pango/pango-break.h>
G_BEGIN_DECLS
@@ -68,6 +69,26 @@ G_STATIC_ASSERT (offsetof (PangoItem, length) == offsetof (PangoItemPrivate, len
G_STATIC_ASSERT (offsetof (PangoItem, num_chars) == offsetof (PangoItemPrivate, num_chars));
G_STATIC_ASSERT (offsetof (PangoItem, analysis) == offsetof (PangoItemPrivate, analysis));
+void pango_analysis_collect_features (const PangoAnalysis *analysis,
+ hb_feature_t *features,
+ guint length,
+ guint *num_features);
+
+GList * pango_itemize_with_font (PangoContext *context,
+ PangoDirection base_dir,
+ const char *text,
+ int start_index,
+ int length,
+ PangoAttrList *attrs,
+ PangoAttrIterator *cached_iter,
+ const PangoFontDescription *desc);
+
+GList * pango_itemize_post_process_items (PangoContext *context,
+ const char *text,
+ PangoLogAttr *log_attrs,
+ GList *items);
+
+
G_END_DECLS
#endif /* __PANGO_ITEM_PRIVATE_H__ */
diff --git a/pango/pango-item.c b/pango/pango-item.c
index 484d5f1f..4b02c277 100644
--- a/pango/pango-item.c
+++ b/pango/pango-item.c
@@ -231,3 +231,71 @@ pango_item_apply_attrs (PangoItem *item,
item->analysis.extra_attrs = g_slist_concat (item->analysis.extra_attrs, attrs);
}
+
+void
+pango_analysis_collect_features (const PangoAnalysis *analysis,
+ hb_feature_t *features,
+ guint length,
+ guint *num_features)
+{
+ GSList *l;
+
+ pango_font_get_features (analysis->font, features, length, num_features);
+
+ for (l = analysis->extra_attrs; l && *num_features < length; l = l->next)
+ {
+ PangoAttribute *attr = l->data;
+ if (attr->klass->type == PANGO_ATTR_FONT_FEATURES)
+ {
+ PangoAttrFontFeatures *fattr = (PangoAttrFontFeatures *) attr;
+ const gchar *feat;
+ const gchar *end;
+ int len;
+
+ feat = fattr->features;
+
+ while (feat != NULL && *num_features < length)
+ {
+ end = strchr (feat, ',');
+ if (end)
+ len = end - feat;
+ else
+ len = -1;
+ if (hb_feature_from_string (feat, len, &features[*num_features]))
+ {
+ features[*num_features].start = attr->start_index;
+ features[*num_features].end = attr->end_index;
+ (*num_features)++;
+ }
+
+ if (end == NULL)
+ break;
+
+ feat = end + 1;
+ }
+ }
+ }
+
+ /* Turn off ligatures when letterspacing */
+ for (l = analysis->extra_attrs; l && *num_features < length; l = l->next)
+ {
+ PangoAttribute *attr = l->data;
+ if (attr->klass->type == PANGO_ATTR_LETTER_SPACING)
+ {
+ hb_tag_t tags[] = {
+ HB_TAG('l','i','g','a'),
+ HB_TAG('c','l','i','g'),
+ HB_TAG('d','l','i','g'),
+ };
+ int i;
+ for (i = 0; i < G_N_ELEMENTS (tags); i++)
+ {
+ features[*num_features].tag = tags[i];
+ features[*num_features].value = 0;
+ features[*num_features].start = attr->start_index;
+ features[*num_features].end = attr->end_index;
+ (*num_features)++;
+ }
+ }
+ }
+}
diff --git a/pango/pango-layout.c b/pango/pango-layout.c
index ba81adf6..9e8ce248 100644
--- a/pango/pango-layout.c
+++ b/pango/pango-layout.c
@@ -4534,13 +4534,14 @@ pango_layout_check_lines (PangoLayout *layout)
g_assert (delim_len >= 0);
state.attrs = itemize_attrs;
- state.items = pango_itemize_with_base_dir (layout->context,
- base_dir,
- layout->text,
- start - layout->text,
- end - start,
- itemize_attrs,
- itemize_attrs ? &iter : NULL);
+ state.items = pango_itemize_with_font (layout->context,
+ base_dir,
+ layout->text,
+ start - layout->text,
+ end - start,
+ itemize_attrs,
+ itemize_attrs ? &iter : NULL,
+ NULL);
apply_attributes_to_items (state.items, shape_attrs);
@@ -4553,6 +4554,11 @@ pango_layout_check_lines (PangoLayout *layout)
layout->log_attrs + start_offset,
layout->n_chars + 1 - start_offset);
+ state.items = pango_itemize_post_process_items (layout->context,
+ layout->text,
+ layout->log_attrs + start_offset,
+ state.items);
+
state.base_dir = base_dir;
state.line_of_par = 1;
state.start_offset = start_offset;
diff --git a/pango/pangofc-fontmap.c b/pango/pangofc-fontmap.c
index 113142fd..076ca291 100644
--- a/pango/pangofc-fontmap.c
+++ b/pango/pangofc-fontmap.c
@@ -1915,8 +1915,33 @@ pango_fc_make_pattern (const PangoFontDescription *description,
if (prgname)
FcPatternAddString (pattern, PANGO_FC_PRGNAME, (FcChar8*) prgname);
- if (variant == PANGO_VARIANT_SMALL_CAPS)
- FcPatternAddString (pattern, PANGO_FC_FONT_FEATURES, (FcChar8*) "smcp=1");
+ switch (variant)
+ {
+ case PANGO_VARIANT_SMALL_CAPS:
+ FcPatternAddString (pattern, PANGO_FC_FONT_FEATURES, (FcChar8*) "smcp=1");
+ break;
+ case PANGO_VARIANT_ALL_SMALL_CAPS:
+ FcPatternAddString (pattern, PANGO_FC_FONT_FEATURES, (FcChar8*) "smcp=1");
+ FcPatternAddString (pattern, PANGO_FC_FONT_FEATURES, (FcChar8*) "c2sc=1");
+ break;
+ case PANGO_VARIANT_PETITE_CAPS:
+ FcPatternAddString (pattern, PANGO_FC_FONT_FEATURES, (FcChar8*) "pcap=1");
+ break;
+ case PANGO_VARIANT_ALL_PETITE_CAPS:
+ FcPatternAddString (pattern, PANGO_FC_FONT_FEATURES, (FcChar8*) "pcap=1");
+ FcPatternAddString (pattern, PANGO_FC_FONT_FEATURES, (FcChar8*) "c2pc=1");
+ break;
+ case PANGO_VARIANT_UNICASE:
+ FcPatternAddString (pattern, PANGO_FC_FONT_FEATURES, (FcChar8*) "unic=1");
+ break;
+ case PANGO_VARIANT_TITLE_CAPS:
+ FcPatternAddString (pattern, PANGO_FC_FONT_FEATURES, (FcChar8*) "titl=1");
+ break;
+ case PANGO_VARIANT_NORMAL:
+ break;
+ default:
+ g_assert_not_reached ();
+ }
return pattern;
}
@@ -2756,6 +2781,8 @@ pango_fc_font_description_from_pattern (FcPattern *pattern, gboolean include_siz
PangoStretch stretch;
double size;
PangoGravity gravity;
+ PangoVariant variant;
+ gboolean all_caps;
FcChar8 *s;
int i;
@@ -2790,7 +2817,8 @@ pango_fc_font_description_from_pattern (FcPattern *pattern, gboolean include_siz
pango_font_description_set_stretch (desc, stretch);
- pango_font_description_set_variant (desc, PANGO_VARIANT_NORMAL);
+ variant = PANGO_VARIANT_NORMAL;
+ all_caps = FALSE;
for (int i = 0; i < 32; i++)
{
@@ -2800,14 +2828,47 @@ pango_fc_font_description_from_pattern (FcPattern *pattern, gboolean include_siz
{
if (strcmp (s, "smcp=1") == 0)
{
- pango_font_description_set_variant (desc, PANGO_VARIANT_SMALL_CAPS);
- break;
+ if (all_caps)
+ variant = PANGO_VARIANT_ALL_SMALL_CAPS;
+ else
+ variant = PANGO_VARIANT_SMALL_CAPS;
+ }
+ else if (strcmp (s, "c2sc=1") == 0)
+ {
+ if (variant == PANGO_VARIANT_SMALL_CAPS)
+ variant = PANGO_VARIANT_ALL_SMALL_CAPS;
+ else
+ all_caps = TRUE;
+ }
+ else if (strcmp (s, "pcap=1") == 0)
+ {
+ if (all_caps)
+ variant = PANGO_VARIANT_ALL_PETITE_CAPS;
+ else
+ variant = PANGO_VARIANT_PETITE_CAPS;
+ }
+ else if (strcmp (s, "c2pc=1") == 0)
+ {
+ if (variant == PANGO_VARIANT_PETITE_CAPS)
+ variant = PANGO_VARIANT_ALL_PETITE_CAPS;
+ else
+ all_caps = TRUE;
+ }
+ else if (strcmp (s, "unic=1") == 0)
+ {
+ variant = PANGO_VARIANT_UNICASE;
+ }
+ else if (strcmp (s, "titl=1") == 0)
+ {
+ variant = PANGO_VARIANT_TITLE_CAPS;
}
}
else
break;
}
+ pango_font_description_set_variant (desc, variant);
+
if (include_size && FcPatternGetDouble (pattern, FC_SIZE, 0, &size) == FcResultMatch)
{
FcMatrix *fc_matrix;
diff --git a/pango/shape.c b/pango/shape.c
index 52a7aabb..02f0f059 100644
--- a/pango/shape.c
+++ b/pango/shape.c
@@ -28,6 +28,7 @@
#include "pango-impl-utils.h"
#include "pango-glyph.h"
+#include "pango-item-private.h"
#include "pango-font-private.h"
#include <hb-ot.h>
@@ -222,72 +223,6 @@ pango_font_get_hb_font_for_context (PangoFont *font,
/* }}} */
/* {{{ Utilities */
-static void
-apply_extra_attributes (GSList *attrs,
- hb_feature_t *features,
- guint length,
- guint *num_features)
-{
- GSList *l;
-
- for (l = attrs; l && *num_features < length; l = l->next)
- {
- PangoAttribute *attr = l->data;
- if (attr->klass->type == PANGO_ATTR_FONT_FEATURES)
- {
- PangoAttrFontFeatures *fattr = (PangoAttrFontFeatures *) attr;
- const gchar *feat;
- const gchar *end;
- int len;
-
- feat = fattr->features;
-
- while (feat != NULL && *num_features < length)
- {
- end = strchr (feat, ',');
- if (end)
- len = end - feat;
- else
- len = -1;
- if (hb_feature_from_string (feat, len, &features[*num_features]))
- {
- features[*num_features].start = attr->start_index;
- features[*num_features].end = attr->end_index;
- (*num_features)++;
- }
-
- if (end == NULL)
- break;
-
- feat = end + 1;
- }
- }
- }
-
- /* Turn off ligatures when letterspacing */
- for (l = attrs; l && *num_features < length; l = l->next)
- {
- PangoAttribute *attr = l->data;
- if (attr->klass->type == PANGO_ATTR_LETTER_SPACING)
- {
- hb_tag_t tags[] = {
- HB_TAG('l','i','g','a'),
- HB_TAG('c','l','i','g'),
- HB_TAG('d','l','i','g'),
- };
- int i;
- for (i = 0; i < G_N_ELEMENTS (tags); i++)
- {
- features[*num_features].tag = tags[i];
- features[*num_features].value = 0;
- features[*num_features].start = attr->start_index;
- features[*num_features].end = attr->end_index;
- (*num_features)++;
- }
- }
- }
-}
-
static PangoShowFlags
find_show_flags (const PangoAnalysis *analysis)
{
@@ -309,16 +244,17 @@ static PangoTextTransform
find_text_transform (const PangoAnalysis *analysis)
{
GSList *l;
+ PangoTextTransform transform = PANGO_TEXT_TRANSFORM_NONE;
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;
+ transform = (PangoTextTransform) ((PangoAttrInt*)attr)->value;
}
- return PANGO_TEXT_TRANSFORM_NONE;
+ return transform;
}
static gboolean
@@ -506,8 +442,7 @@ pango_hb_shape (const char *item_text,
hb_buffer_add (hb_buffer, '-', hyphen_index);
}
- pango_font_get_features (analysis->font, features, G_N_ELEMENTS (features), &num_features);
- apply_extra_attributes (analysis->extra_attrs, features, G_N_ELEMENTS (features), &num_features);
+ pango_analysis_collect_features (analysis, features, G_N_ELEMENTS (features), &num_features);
hb_shape (hb_font, hb_buffer, features, num_features);
diff --git a/tests/test-font.c b/tests/test-font.c
index cd243a95..bfe321a0 100644
--- a/tests/test-font.c
+++ b/tests/test-font.c
@@ -294,27 +294,65 @@ test_roundtrip_small_caps (void)
return;
}
- desc = pango_font_description_from_string ("Cantarell Small-Caps 11");
-
- g_assert_true (pango_font_description_get_variant (desc) == PANGO_VARIANT_SMALL_CAPS);
-
fontmap = pango_cairo_font_map_get_default ();
context = pango_font_map_create_context (fontmap);
+ desc = pango_font_description_from_string ("Cantarell Small-Caps 11");
+ g_assert_true (pango_font_description_get_variant (desc) == PANGO_VARIANT_SMALL_CAPS);
+
font = pango_context_load_font (context, desc);
desc2 = pango_font_describe (font);
+ num = 0;
pango_font_get_features (font, features, G_N_ELEMENTS (features), &num);
g_assert_true (num == 1);
g_assert_true (features[0].tag == HB_TAG ('s', 'm', 'c', 'p'));
g_assert_true (features[0].value == 1);
g_assert_true (pango_font_description_get_variant (desc2) == PANGO_VARIANT_SMALL_CAPS);
+ g_assert_true (pango_font_description_equal (desc2, desc));
+ pango_font_description_free (desc2);
+ g_object_unref (font);
+ pango_font_description_free (desc);
+
+ desc = pango_font_description_from_string ("Cantarell All-Small-Caps 11");
+ g_assert_true (pango_font_description_get_variant (desc) == PANGO_VARIANT_ALL_SMALL_CAPS);
+
+ font = pango_context_load_font (context, desc);
+ desc2 = pango_font_describe (font);
+
+ num = 0;
+ pango_font_get_features (font, features, G_N_ELEMENTS (features), &num);
+ g_assert_true (num == 2);
+ g_assert_true (features[0].tag == HB_TAG ('s', 'm', 'c', 'p'));
+ g_assert_true (features[0].value == 1);
+ g_assert_true (features[1].tag == HB_TAG ('c', '2', 's', 'c'));
+ g_assert_true (features[1].value == 1);
+ g_assert_true (pango_font_description_get_variant (desc2) == PANGO_VARIANT_ALL_SMALL_CAPS);
+ g_assert_true (pango_font_description_equal (desc2, desc));
+
+ pango_font_description_free (desc2);
+ g_object_unref (font);
+ pango_font_description_free (desc);
+
+ desc = pango_font_description_from_string ("Cantarell Unicase 11");
+ g_assert_true (pango_font_description_get_variant (desc) == PANGO_VARIANT_UNICASE);
+
+ font = pango_context_load_font (context, desc);
+ desc2 = pango_font_describe (font);
+
+ num = 0;
+ pango_font_get_features (font, features, G_N_ELEMENTS (features), &num);
+ g_assert_true (num == 1);
+ g_assert_true (features[0].tag == HB_TAG ('u', 'n', 'i', 'c'));
+ g_assert_true (features[0].value == 1);
+ g_assert_true (pango_font_description_get_variant (desc2) == PANGO_VARIANT_UNICASE);
g_assert_true (pango_font_description_equal (desc2, desc));
pango_font_description_free (desc2);
g_object_unref (font);
pango_font_description_free (desc);
+
g_object_unref (context);
}