[gtk/backport-font-feature-pango-hb: 2/8] gtkfontchooserwidget.c: Always enable font features with later Pango
- From: Chun-wei Fan <fanchunwei src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/backport-font-feature-pango-hb: 2/8] gtkfontchooserwidget.c: Always enable font features with later Pango
- Date: Thu, 25 Nov 2021 10:59:41 +0000 (UTC)
commit c13bc9ac37cf3d6c4557ce67afad814507784736
Author: Chun-wei Fan <fanchunwei src gnome org>
Date: Thu Nov 25 12:28:27 2021 +0800
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.
gtk/gtkfontchooserwidget.c | 339 ++++++++++++++++++++++++++++++++++++++-------
1 file changed, 286 insertions(+), 53 deletions(-)
---
diff --git a/gtk/gtkfontchooserwidget.c b/gtk/gtkfontchooserwidget.c
index ed52352d7a..4347af862c 100644
--- a/gtk/gtkfontchooserwidget.c
+++ b/gtk/gtkfontchooserwidget.c
@@ -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;
-
- 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;
+ ft_face = pango_fc_font_lock_face (PANGO_FC_FONT (pango_font));
- 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;
+ 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);
}
- g_free (coords);
- free (ft_mm_var);
+ 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);
+
+ 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;
+ }
+
+ g_free (axes);
+#endif
- pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font));
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))
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]