gtkfontchooserwidget.c: Always enable font features with later Pango

This is a backport of the code in GTK4 where we can use the font
features that is given to us via HarfBuzz if we have Pango 1.44.x and
HarfBuzz 2.2.0 or later installed, even if we do not have PangoFT2
aavilable.

Since Pango 1.44.x depends on HarfBuzz for all platforms after 1.44.0,
we could take advantage of that and build the support in
GtkFontChooserWidget.

Add a directive that is to be used by Visual Studio compilers via the
Visual Studio projects to link to harfbuzz.lib automatically, so that
this support can be linked properly if we have the required Pango and
HarfBuzz headers and lib's installed.  Meson builds via Visual Studio
should handle this automatically, since pkg-config is being used there.

Since HAVE_PANGOFT2 and HAVE_HARFBUZZ is not defined by default in the
Visaul Studio projects, we will leave it up to the user to enable them
themselves and put in pangoft2-1.0.lib and harfbuzz.lib in the project
settings by themselves, or they could use Meson, as we did before.
This commit is contained in:
Chun-wei Fan
2021-11-25 12:28:27 +08:00
parent 6c4cc93121
commit e1422d73b1

View File

@@ -53,7 +53,15 @@
#include "gtkcombobox.h"
#include "gtkgesturemultipress.h"
#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
#if (PANGO_VERSION_CHECK(1,44,0) && HB_VERSION_ATLEAST(2,2,0)) || \
(defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT))
#define HAVE_FONT_FEATURES 1
#if !(PANGO_VERSION_CHECK(1,44,0) && HB_VERSION_ATLEAST(2,2,0))
#define FONT_FEATURES_USE_PANGOFT2 1
#endif
#endif
#ifdef FONT_FEATURES_USE_PANGOFT2
#include <pango/pangofc-font.h>
#include <hb.h>
#include <hb-ot.h>
@@ -62,6 +70,12 @@
#include <freetype/ftmm.h>
#include "language-names.h"
#include "script-names.h"
#elif defined (HAVE_FONT_FEATURES)
#include <hb-ot.h>
#if defined (_MSC_VER) && defined (__MSVC_PROJECTS__)
#pragma comment(lib, "harfbuzz")
#endif
#endif
#include "open-type-layout.h"
@@ -763,7 +777,7 @@ change_tweak (GSimpleAction *action,
g_simple_action_set_state (action, state);
}
#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
#ifdef HAVE_FONT_FEATURES
typedef struct {
guint32 tag;
@@ -823,7 +837,7 @@ gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
gtk_widget_init_template (GTK_WIDGET (fontchooser));
#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
#ifdef HAVE_FONT_FEATURES
priv->axes = g_hash_table_new_full (axis_hash, axis_equal, NULL, axis_free);
#endif
@@ -864,7 +878,7 @@ gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
/* Load data and set initial style-dependent parameters */
gtk_font_chooser_widget_load_fonts (fontchooser, TRUE);
#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
#ifdef HAVE_FONT_FEATURES
gtk_font_chooser_widget_populate_features (fontchooser);
#endif
gtk_font_chooser_widget_set_cell_size (fontchooser);
@@ -1475,7 +1489,7 @@ gtk_font_chooser_widget_ensure_selection (GtkFontChooserWidget *fontchooser)
}
}
#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
#ifdef HAVE_FONT_FEATURES
/* OpenType variations */
@@ -1533,6 +1547,7 @@ adjustment_changed (GtkAdjustment *adjustment,
priv->updating_variations = FALSE;
}
#ifdef FONT_FEATURES_USE_PANGOFT2
static gboolean
should_show_axis (FT_Var_Axis *ax)
{
@@ -1549,22 +1564,149 @@ is_named_instance (FT_Face face)
return (face->face_index >> 16) > 0;
}
#define TAG_WIDTH FT_MAKE_TAG ('w', 'd', 't', 'h')
#define TAG_WEIGHT FT_MAKE_TAG ('w', 'g', 'h', 't')
#define TAG_ITALIC FT_MAKE_TAG ('i', 't', 'a', 'l')
#define TAG_SLANT FT_MAKE_TAG ('s', 'l', 'n', 't')
#define TAG_OPTICAL_SIZE FT_MAKE_TAG ('o', 'p', 's', 'z')
#else
static gboolean
should_show_axis (hb_ot_var_axis_info_t *ax)
{
if (ax->flags & HB_OT_VAR_AXIS_FLAG_HIDDEN)
return FALSE;
return TRUE;
}
static gboolean
is_named_instance (hb_face_t *face)
{
/* FIXME */
return FALSE;
}
#define TAG_WIDTH HB_OT_TAG_VAR_AXIS_WIDTH
#define TAG_WEIGHT HB_OT_TAG_VAR_AXIS_WEIGHT
#define TAG_ITALIC HB_OT_TAG_VAR_AXIS_ITALIC
#define TAG_SLANT HB_OT_TAG_VAR_AXIS_SLANT
#define TAG_OPTICAL_SIZE HB_OT_TAG_VAR_AXIS_OPTICAL_SIZE
#endif
static struct {
guint32 tag;
const char *name;
} axis_names[] = {
{ FT_MAKE_TAG ('w', 'd', 't', 'h'), N_("Width") },
{ FT_MAKE_TAG ('w', 'g', 'h', 't'), N_("Weight") },
{ FT_MAKE_TAG ('i', 't', 'a', 'l'), N_("Italic") },
{ FT_MAKE_TAG ('s', 'l', 'n', 't'), N_("Slant") },
{ FT_MAKE_TAG ('o', 'p', 's', 'z'), N_("Optical Size") },
{ TAG_WIDTH, N_("Width") },
{ TAG_WEIGHT, N_("Weight") },
{ TAG_ITALIC, N_("Italic") },
{ TAG_SLANT, N_("Slant") },
{ TAG_OPTICAL_SIZE, N_("Optical Size") },
};
#undef TAG_WIDTH
#undef TAG_WEIGHT
#undef TAG_ITALIC
#undef TAG_SLANT
#undef TAG_OPTICAL_SIZE
#ifdef FONT_FEATURES_USE_PANGOFT2
#define FONT_FACE_TYPE FT_Face
#define FONT_VAR_AXIS_TYPE FT_Var_Axis
#define FONT_VALUE_TYPE FT_Fixed
/*
* We actually don't bother about the FT_Face here, but we use this so that we can have a single
* version of add_axis() taylored to PangoFT2 or Pango with HarfBuzz integrated
*/
static void *
get_font_name (FT_Face face,
FT_Var_Axis *ax,
const char *result)
{
result = ax->name;
}
static const float
get_float_value (FT_Fixed value)
{
return FixedToFloat (value);
}
static const float
get_axis_float_max (FT_Var_Axis *ax)
{
return FixedToFloat (ax->maximum);
}
static const float
get_axis_float_min (FT_Var_Axis *ax)
{
return FixedToFloat (ax->minimum);
}
static const float
get_axis_float_default (FT_Var_Axis *ax)
{
return FixedToFloat (ax->def);
}
#else
#define FONT_FACE_TYPE hb_face_t *
#define FONT_VAR_AXIS_TYPE hb_ot_var_axis_info_t
#define FONT_VALUE_TYPE int
static void
get_font_name (hb_face_t *face,
hb_ot_var_axis_info_t *ax,
const char *name)
{
char buffer[20];
unsigned int buffer_len = 20;
hb_ot_name_get_utf8 (face, ax->name_id, HB_LANGUAGE_INVALID, &buffer_len, buffer);
name = buffer;
}
#define get_float_value(x) x
static const float
get_axis_float_max (hb_ot_var_axis_info_t *ax)
{
return ax->max_value;
}
static const float
get_axis_float_min (hb_ot_var_axis_info_t *ax)
{
return ax->min_value;
}
static const float
get_axis_float_default (hb_ot_var_axis_info_t *ax)
{
return ax->default_value;
}
/* FIXME: This doesn't work if the font has an avar table */
static float
denorm_coord (hb_ot_var_axis_info_t *axis, int coord)
{
float r = coord / 16384.0;
if (coord < 0)
return axis->default_value + r * (axis->default_value - axis->min_value);
else
return axis->default_value + r * (axis->max_value - axis->default_value);
}
#endif
static gboolean
add_axis (GtkFontChooserWidget *fontchooser,
FT_Face face,
FT_Var_Axis *ax,
FT_Fixed value,
FONT_FACE_TYPE face,
FONT_VAR_AXIS_TYPE *ax,
FONT_VALUE_TYPE value,
int row)
{
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
@@ -1576,7 +1718,8 @@ add_axis (GtkFontChooserWidget *fontchooser,
axis->tag = ax->tag;
axis->fontchooser = GTK_WIDGET (fontchooser);
name = ax->name;
get_font_name (face, ax, name);
for (i = 0; i < G_N_ELEMENTS (axis_names); i++)
{
if (axis_names[i].tag == ax->tag)
@@ -1585,18 +1728,19 @@ add_axis (GtkFontChooserWidget *fontchooser,
break;
}
}
axis->label = gtk_label_new (name);
gtk_widget_show (axis->label);
gtk_widget_set_halign (axis->label, GTK_ALIGN_START);
gtk_widget_set_valign (axis->label, GTK_ALIGN_BASELINE);
gtk_grid_attach (GTK_GRID (priv->axis_grid), axis->label, 0, row, 1, 1);
axis->adjustment = gtk_adjustment_new ((double)FixedToFloat(value),
(double)FixedToFloat(ax->minimum),
(double)FixedToFloat(ax->maximum),
axis->adjustment = gtk_adjustment_new ((double)get_float_value (value),
(double)get_axis_float_min (ax),
(double)get_axis_float_max (ax),
1.0, 10.0, 0.0);
axis->scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, axis->adjustment);
gtk_widget_show (axis->scale);
gtk_scale_add_mark (GTK_SCALE (axis->scale), (double)FixedToFloat(ax->def), GTK_POS_TOP, NULL);
gtk_scale_add_mark (GTK_SCALE (axis->scale), (double)get_axis_float_default (ax), GTK_POS_TOP, NULL);
gtk_widget_set_valign (axis->scale, GTK_ALIGN_BASELINE);
gtk_widget_set_hexpand (axis->scale, TRUE);
gtk_widget_set_size_request (axis->scale, 100, -1);
@@ -1612,6 +1756,7 @@ add_axis (GtkFontChooserWidget *fontchooser,
adjustment_changed (axis->adjustment, axis);
g_signal_connect (axis->adjustment, "value-changed", G_CALLBACK (adjustment_changed), axis);
if (is_named_instance (face) || !should_show_axis (ax))
{
gtk_widget_hide (axis->label);
@@ -1629,9 +1774,21 @@ gtk_font_chooser_widget_update_font_variations (GtkFontChooserWidget *fontchoose
{
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
PangoFont *pango_font;
#ifdef FONT_FEATURES_USE_PANGOFT2
FT_Face ft_face;
FT_MM_Var *ft_mm_var;
FT_Error ret;
#else
hb_font_t *hb_font;
hb_face_t *hb_face;
const int *coords;
unsigned int n_coords;
hb_ot_var_axis_info_t *axes;
int num_axes, i;
#endif
gboolean has_axis = FALSE;
if (priv->updating_variations)
@@ -1645,39 +1802,72 @@ gtk_font_chooser_widget_update_font_variations (GtkFontChooserWidget *fontchoose
pango_font = pango_context_load_font (gtk_widget_get_pango_context (GTK_WIDGET (fontchooser)),
priv->font_desc);
ft_face = pango_fc_font_lock_face (PANGO_FC_FONT (pango_font));
ret = FT_Get_MM_Var (ft_face, &ft_mm_var);
if (ret == 0)
#ifdef FONT_FEATURES_USE_PANGOFT2
if (PANGO_IS_FC_FONT (pango_font))
{
int i;
FT_Fixed *coords;
ft_face = pango_fc_font_lock_face (PANGO_FC_FONT (pango_font));
coords = g_new (FT_Fixed, ft_mm_var->num_axis);
for (i = 0; i < ft_mm_var->num_axis; i++)
coords[i] = ft_mm_var->axis[i].def;
if (ft_face->face_index > 0)
ret = FT_Get_MM_Var (ft_face, &ft_mm_var);
if (ret == 0)
{
int instance_id = ft_face->face_index >> 16;
if (instance_id && instance_id <= ft_mm_var->num_namedstyles)
int i;
FT_Fixed *coords;
coords = g_new (FT_Fixed, ft_mm_var->num_axis);
for (i = 0; i < ft_mm_var->num_axis; i++)
coords[i] = ft_mm_var->axis[i].def;
if (ft_face->face_index > 0)
{
FT_Var_Named_Style *instance = &ft_mm_var->namedstyle[instance_id - 1];
memcpy (coords, instance->coords, ft_mm_var->num_axis * sizeof (*coords));
int instance_id = ft_face->face_index >> 16;
if (instance_id && instance_id <= ft_mm_var->num_namedstyles)
{
FT_Var_Named_Style *instance = &ft_mm_var->namedstyle[instance_id - 1];
memcpy (coords, instance->coords, ft_mm_var->num_axis * sizeof (*coords));
}
}
for (i = 0; i < ft_mm_var->num_axis; i++)
{
if (add_axis (fontchooser, ft_face, &ft_mm_var->axis[i], coords[i], i + 4))
has_axis = TRUE;
}
g_free (coords);
free (ft_mm_var);
}
for (i = 0; i < ft_mm_var->num_axis; i++)
{
if (add_axis (fontchooser, ft_face, &ft_mm_var->axis[i], coords[i], i + 4))
has_axis = TRUE;
}
pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font));
}
#else
hb_font = pango_font_get_hb_font (pango_font);
hb_face = hb_font_get_face (hb_font);
g_free (coords);
free (ft_mm_var);
if (!hb_ot_var_has_data (hb_face))
return FALSE;
coords = hb_font_get_var_coords_normalized (hb_font, &n_coords);
num_axes = hb_ot_var_get_axis_count (hb_face);
axes = g_new0 (hb_ot_var_axis_info_t, num_axes);
hb_ot_var_get_axis_infos (hb_face, 0, &num_axes, axes);
for (i = 0; i < num_axes; i ++)
{
float value;
if (coords && i < n_coords)
value = denorm_coord (&axes[i], coords[i]);
else
value = axes[i].default_value;
if (add_axis (fontchooser, hb_font_get_face (hb_font), &axes[i], value, i + 4))
has_axis = TRUE;
}
pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font));
g_free (axes);
#endif
g_object_unref (pango_font);
return has_axis;
@@ -1807,6 +1997,7 @@ feat_pressed (GtkGesture *gesture,
static char *
find_affected_text (hb_tag_t feature_tag,
hb_font_t *hb_font,
hb_face_t *hb_face,
hb_tag_t script_tag,
hb_tag_t lang_tag,
@@ -1820,7 +2011,11 @@ find_affected_text (hb_tag_t feature_tag,
chars = g_string_new ("");
hb_ot_layout_table_find_script (hb_face, HB_OT_TAG_GSUB, script_tag, &script_index);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
hb_ot_layout_script_find_language (hb_face, HB_OT_TAG_GSUB, script_index, lang_tag, &lang_index);
G_GNUC_END_IGNORE_DEPRECATIONS
if (hb_ot_layout_language_find_feature (hb_face, HB_OT_TAG_GSUB, script_index, lang_index, feature_tag, &feature_index))
{
unsigned int lookup_indexes[32];
@@ -1840,8 +2035,8 @@ find_affected_text (hb_tag_t feature_tag,
hb_set_t* glyphs_input = NULL;
hb_set_t* glyphs_after = NULL;
hb_set_t* glyphs_output = NULL;
hb_font_t *hb_font = NULL;
hb_codepoint_t gid;
gboolean destroy_font = FALSE;
glyphs_input = hb_set_create ();
@@ -1854,8 +2049,16 @@ find_affected_text (hb_tag_t feature_tag,
glyphs_after,
glyphs_output);
hb_font = hb_font_create (hb_face);
hb_ft_font_set_funcs (hb_font);
#ifdef FONT_FEATURES_USE_PANGOFT
if (hb_font == NULL)
{
/* only applicable if we are doing this via PangoFT2, where we need to create the hb_font_t */
hb_font = hb_font_create (hb_face);
hb_ft_font_set_funcs (hb_font);
destroy_font = TRUE;
}
#endif
gid = -1;
while (hb_set_next (glyphs_input, &gid)) {
@@ -1876,7 +2079,9 @@ find_affected_text (hb_tag_t feature_tag,
}
}
hb_set_destroy (glyphs_input);
hb_font_destroy (hb_font);
if (destroy_font)
hb_font_destroy (hb_font);
}
}
@@ -1885,6 +2090,7 @@ find_affected_text (hb_tag_t feature_tag,
static void
update_feature_example (FeatureItem *item,
hb_font_t *hb_font,
hb_face_t *hb_face,
hb_tag_t script_tag,
hb_tag_t lang_tag,
@@ -1937,9 +2143,9 @@ update_feature_example (FeatureItem *item,
else if (strcmp (item->name, "zero") == 0)
input = g_strdup ("0");
else if (strcmp (item->name, "nalt") == 0)
input = find_affected_text (item->tag, hb_face, script_tag, lang_tag, 3);
input = find_affected_text (item->tag, hb_font, hb_face, script_tag, lang_tag, 3);
else
input = find_affected_text (item->tag, hb_face, script_tag, lang_tag, 10);
input = find_affected_text (item->tag, hb_font, hb_face, script_tag, lang_tag, 10);
if (input[0] != '\0')
{
@@ -2156,8 +2362,12 @@ gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
{
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
PangoFont *pango_font;
#ifdef FONT_FEATURES_USE_PANGOFT2
FT_Face ft_face;
hb_font_t *hb_font;
#endif
hb_font_t *hb_font = NULL;
hb_tag_t script_tag;
hb_tag_t lang_tag;
guint script_index = 0;
@@ -2165,6 +2375,7 @@ gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
int i, j;
GList *l;
gboolean has_feature = FALSE;
gboolean cleanup_hb_face = FALSE;
for (l = priv->feature_items; l; l = l->next)
{
@@ -2178,8 +2389,17 @@ gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
pango_font = pango_context_load_font (gtk_widget_get_pango_context (GTK_WIDGET (fontchooser)),
priv->font_desc);
ft_face = pango_fc_font_lock_face (PANGO_FC_FONT (pango_font)),
hb_font = hb_ft_font_create (ft_face, NULL);
#ifdef FONT_FEATURE_USE_PANGOFT2
if (PANGO_IS_FC_FONT (pango_font))
{
ft_face = pango_fc_font_lock_face (PANGO_FC_FONT (pango_font)),
hb_font = hb_ft_font_create (ft_face, NULL);
cleanup_hb_face = TRUE;
}
#else
hb_font = pango_font_get_hb_font (pango_font);
#endif
if (hb_font)
{
@@ -2197,7 +2417,11 @@ gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
for (i = 0; i < 2; i++)
{
hb_ot_layout_table_find_script (hb_face, table[i], script_tag, &script_index);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
hb_ot_layout_script_find_language (hb_face, table[i], script_index, lang_tag, &lang_index);
G_GNUC_END_IGNORE_DEPRECATIONS
count = G_N_ELEMENTS (features);
hb_ot_layout_language_get_feature_tags (hb_face,
table[i],
@@ -2214,6 +2438,8 @@ gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
for (l = priv->feature_items; l; l = l->next)
{
FeatureItem *item = l->data;
hb_font_t *hb_font2 = NULL;
if (item->tag != features[j])
continue;
@@ -2221,7 +2447,10 @@ gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
gtk_widget_show (item->top);
gtk_widget_show (gtk_widget_get_parent (item->top));
update_feature_example (item, hb_face, script_tag, lang_tag, priv->font_desc);
if (!cleanup_hb_face)
hb_font2 = hb_font;
update_feature_example (item, hb_font2, hb_face, script_tag, lang_tag, priv->font_desc);
if (GTK_IS_RADIO_BUTTON (item->feat))
{
@@ -2235,10 +2464,14 @@ gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
}
}
hb_face_destroy (hb_face);
if (cleanup_hb_face)
hb_face_destroy (hb_face);
}
pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font));
#if FONT_FEATURE_USE_PANGOFT2
if (PANGO_IS_FC_FONT (pango_font))
pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font));
#endif
g_object_unref (pango_font);
return has_feature;
@@ -2338,7 +2571,7 @@ gtk_font_chooser_widget_merge_font_desc (GtkFontChooserWidget *fontchooser
gtk_font_chooser_widget_update_marks (fontchooser);
#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
#ifdef HAVE_FONT_FEATURES
if (gtk_font_chooser_widget_update_font_features (fontchooser))
has_tweak = TRUE;
if (gtk_font_chooser_widget_update_font_variations (fontchooser))