GNOME Bugzilla – Bug 738505
Add fontfeatures support in PangoAttributes and markup
Last modified: 2015-08-11 04:43:19 UTC
(I thought I filed an RFE for that but seems not) Pango has supported fontfeatures with fontconfig though, no support in PangoAttributes. that is hard to accomplish addidng kinda font-features-setting with CSS in GTK+. also nice to see that on Pango markup as well.
There should be a bug for it already. Anyway, yes, needs to be done.
Created attachment 298964 [details] [review] Initial patch to check the behavior That might needs to be polished but hope that would be a good start.
Review of attachment 298964 [details] [review]: I think it would be nice to use the syntax from http://www.w3.org/TR/css3-fonts/#propdef-font-feature-setting ::: modules/basic/basic-fc.c @@ +401,3 @@ + + for (tmp_attrs = analysis->extra_attrs; tmp_attrs; tmp_attrs = tmp_attrs->next) + { features here is currently a fixed-size array of length 8, so you should probably terminate this loop if num_features gets too big, or arrange for features to grow. ::: pango/pango-attributes.h @@ +422,3 @@ + PangoAttribute attr; + gchar *tag; + gchar *value; I think value could probably be an integer here.
Created attachment 299064 [details] [review] Bug 738505 - Add fontfeatures support in PangoAttributes and markup
Here's a slight revision of your patch that has a few of the suggested changes: - store feature attributes as tag+int value - parse a syntax more like css: <span font_feature_settings="dlig on, vert off, c2sc, swsh 2"> - be careful about overrun of the features array
I've written a small demo app here: https://git.gnome.org/browse/gtk+/log/?h=wip/matthiasc/font-features
Thanks. looks good to me. though I'm not sure how often "overrun" happens. think of complaint about dropping the feature tag without any warnings, maybe good to grow the array perhaps if tags was too many.
Let me review this quickly.
Akira, do you have any recommendations for free opentype fonts to test this with ?
Shouldn’t hb_feature_from_string() be used here as it already supports the CSS syntax?
(11:41:42 AM) Behdad Esfahbod: mclasen: 1. try to use hb api as khaled noted. we'll make hb a hard requirement at some point. in the mean time feel free to copy it. (11:42:02 AM) Behdad Esfahbod: mclasen: 2. I was under the impression that every part of a layout can have only one attribute of each type (11:42:11 AM) Behdad Esfahbod: that means you can't have multiple tag:value features. (11:42:19 AM) mclasen: I just copied that tag function because I didn't want to fiddle with LDFLAGS (11:42:23 AM) Behdad Esfahbod: my recommendation would be to make the PangoAttrFontFeatures take a string. (11:42:34 AM) Behdad Esfahbod: and parse the string during shaping. (11:42:39 AM) Behdad Esfahbod: we do that in android for example (11:42:59 AM) Behdad Esfahbod: (and rename it to PangoAttrFontFeatureSettings or FontFeatures as opposed to just Features) (11:43:11 AM) mclasen: hmm, then you get markup syntax issues bleed into the shaper (11:43:23 AM) Behdad Esfahbod: no it's not a markup limitation (11:43:31 AM) Behdad Esfahbod: it's a pango layout attributes afair (11:43:34 AM) Behdad Esfahbod: maybe I'm wrong (11:43:44 AM) Behdad Esfahbod: I'll check in a bit. got to run right now.
(In reply to Khaled Hosny from comment #10) > Shouldn’t hb_feature_from_string() be used here as it already supports the > CSS syntax? Sounds like a good idea. I wasn't aware of this API.
Created attachment 299130 [details] [review] Add support for OpenType features in attributes and markup This commit adds support for a font_features attribute in Pango markup, and a new PangoAttributeFontFeatures attribute to hold it. The syntax of the value is very similar to the font-feature-settings CSS3 property: a comma-separated list of font feature strings. The individual font features are parsed with hb_feature_from_string().
Version 3, along the lines outlined in comment 11
(In reply to Matthias Clasen from comment #9) > Akira, do you have any recommendations for free opentype fonts to test this > with ? I tested test-feature-tag.markup with IPAexGothic font which is included in ipa-ex-gothic-fonts package in Fedora.
Review of attachment 299130 [details] [review]: ::: pango/pango-attributes.c @@ +1956,3 @@ PangoAttribute *old_attr = tmp_list2->data; + if (attr->klass->type == old_attr->klass->type && + attr->klass->type != PANGO_ATTR_FONT_FEATURES) Given that we don't want to have multiple same attributes in the layout, we need to update here. particularly to append the feature tags if already there I think.
(In reply to Akira TAGOH from comment #16) > Review of attachment 299130 [details] [review] [review]: > > ::: pango/pango-attributes.c > @@ +1956,3 @@ > PangoAttribute *old_attr = tmp_list2->data; > + if (attr->klass->type == old_attr->klass->type && > + attr->klass->type != PANGO_ATTR_FONT_FEATURES) > > Given that we don't want to have multiple same attributes in the layout, we > need to update here. particularly to append the feature tags if already > there I think. Looking at the way the other attributes are handled in that loop makes me believe that the intended semantics here is 'last one wins', while the current handling for font features is 'first on wins'. Not sure merging makes sense, given that we don't even parse the string here. Behdad ? Clearly, this needs a) a comment explaining the expected semantics and b) tests.
never mind about 'last one wins' - I was wrong. But still, writing that merging code will not be trivial.
Sure. though there has never been attributes that have the same key and different values not conflicting each other. plus, we may need to take care of dropping duplicate tags perhaps so that there are limitations to the amount of features at this moment. repeating same tags becomes flowing the space with it.
I wrote some tests (testattributes.c) to understand how attribute lists actually work, wrt. to conflicts and priorities. My takeaways: - It would be somewhat silly to write 'merging' code for feature strings, when pango already handles all the merging and coalescing for attributes - Currently, the resolution is entirely based on attribute type I see a few options: - Go back to parsing the feature string at markup parsing time, and teach pango that font feature attributes only conflict if their ot tag is different - Register separate attributes per ot tag. This is hard because we need to allocate a separate class struct per type, and keep those somewhere - Give up on proper merging of font features - they are most likely going to be used globally anyway
- Go back to parsing the feature string at markup parsing time, and teach pango that font feature attributes only conflict if their ot tag is different Here I meant "is the same" of course
*** Bug 460831 has been marked as a duplicate of this bug. ***
So here is why I never added fontfeatures to pango, because, like you, I wanted to add it properly. Note that CSS also has this deficiency, that is, you cannot add style attributes to a span to enable a font feature without clearing whatever features it was inheriting from its parent elements. Note that pango-markup.c to some extent takes care of a similar problem, which happens with any markup that affects the PangoFontDescription. One way to fix this is to add a new function to attribute types to "reduce" or "merge" two of them. Then one would only need to join the two strings for font-feature-settings using ",".
This is relevant: https://bugzilla.gnome.org/show_bug.cgi?id=446355 I see PangoAttrClass is public and has no room for a new merge() method. One possibility is to add a new PangoAttrClassExtended version, and differentiate by using the high bit (or some other indicator) of the PangoAttrType. Then add pango_attr_type_register_extended(). Then add pango_attribute_merge(a, b). a and b should be of the same attr type. If the attr type is extended and implements merge(), this would call merge on it, which should return a new attribute that has b merged on top of a. If not, it returns a copy of b. Not sure how to reduce the number of copies... Then update pango_attr_list_change() to call merge() when an attribute exists already.
Looking at https://git.gnome.org/browse/pango/commit/?id=1eda6add15f4694372d0ebe1de7904045292c5ea, there is no splitting and merging of attribute ranges happening at all, so my v3 patch is probably the best we can do here. We'll have the same limitation as css: font-features are all-or-nothing, can't do partial inheritance. But it appears the change I cite above already made it so for all the font-related attributes anyway. Reverting that commit would be a prerequisite for doing any more intelligent merging and inheritance.
just for the reference, we have the fi ligature issue here: https://bugzilla.redhat.com/show_bug.cgi?id=1099829 Once this feature is merged, we should have "liga off" in the text entry by default.
(In reply to Akira TAGOH from comment #26) > Once this feature is merged, we should have "liga off" in the text entry by > default. This will break fonts that rely on liga for proper shaping (many Arabic fonts do).
(In reply to Akira TAGOH from comment #26) > just for the reference, we have the fi ligature issue here: > https://bugzilla.redhat.com/show_bug.cgi?id=1099829 > > Once this feature is merged, we should have "liga off" in the text entry by > default. That bug looks like a font or text rendering issue. Even if it's not and ligatures are still not desirable in that particular case, they should be turned off there. I don't see how that bug justifies disabling ligatures by default! That would be unacceptable.
I don't see any reason to turn ligatures off, just because one user doesn't like them.
I might have a few hours to work on this today or tomorrow. Matthias, do you have any more comments I should take into account while fixing this? I don't have a full plan yet, but I guess I'll start by adding the merge thing and see how that would work. Touching attr_list_change frightens me but it might not be as bad as I think...
no more comments from me
FWIW here's a pad from the OpenType features session at Libre Graphics Meeting 2015 in Toronto: http://piratepad.net/GprTYzpjgr
Comment on attachment 298964 [details] [review] Initial patch to check the behavior >From 281a0dec828220db19c02e73775bfbe0fcd0bbd6 Mon Sep 17 00:00:00 2001 >From: Akira TAGOH <akira@tagoh.org> >Date: Tue, 10 Mar 2015 19:50:54 +0900 >Subject: [PATCH] Bug 738505 - Add fontfeatures support in PangoAttributes and > markup > >https://bugzilla.gnome.org/show_bug.cgi?id=738505 >--- > modules/basic/basic-fc.c | 22 +++++++++++++ > pango-view/test-feature-tag.markup | 65 +++++++++++++++++++++++++++++++++++++ > pango/pango-attributes.c | 66 +++++++++++++++++++++++++++++++++++++- > pango/pango-attributes.h | 22 ++++++++++++- > pango/pango-markup.c | 22 +++++++++++++ > 5 files changed, 195 insertions(+), 2 deletions(-) > create mode 100644 pango-view/test-feature-tag.markup > >diff --git a/modules/basic/basic-fc.c b/modules/basic/basic-fc.c >index 93c4478..048939d 100644 >--- a/modules/basic/basic-fc.c >+++ b/modules/basic/basic-fc.c >@@ -28,6 +28,7 @@ > #define PANGO_ENABLE_BACKEND 1 /* XXX */ > > #include "pango-engine.h" >+#include "pango-attributes.h" > #include "pango-utils.h" > #include "pangofc-fontmap.h" > #include "pangofc-font.h" >@@ -394,6 +395,27 @@ basic_engine_shape (PangoEngineShape *engine G_GNUC_UNUSED, > num_features++; > } > } >+ if (analysis->extra_attrs) >+ { >+ GSList *tmp_attrs; >+ >+ for (tmp_attrs = analysis->extra_attrs; tmp_attrs; tmp_attrs = tmp_attrs->next) >+ { >+ if (((PangoAttribute *) tmp_attrs->data)->klass->type == PANGO_ATTR_FEATURE) >+ { >+ const PangoAttrFeature *fattr = (const PangoAttrFeature *) tmp_attrs->data; >+ gboolean f = g_ascii_strcasecmp (fattr->value, "1") == 0 || >+ g_ascii_strcasecmp (fattr->value, "true") == 0 || >+ g_ascii_strcasecmp (fattr->value, "yes") == 0 ? TRUE : FALSE; >+ >+ features[num_features].tag = hb_tag_from_string (fattr->tag, -1); >+ features[num_features].value = f; >+ features[num_features].start = 0; >+ features[num_features].end = (unsigned int) -1; >+ num_features++; >+ } >+ } >+ } > > hb_shape (hb_font, hb_buffer, features, num_features); > >diff --git a/pango-view/test-feature-tag.markup b/pango-view/test-feature-tag.markup >new file mode 100644 >index 0000000..cc2ba1b >--- /dev/null >+++ b/pango-view/test-feature-tag.markup >@@ -0,0 +1,65 @@ >+<span font_desc="IPAexGothic 8"> >+ASCII: >+0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ >+Full Widths: >+<span font_feature_settings="fwid=1"> >+0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ >+</span> >+Half Widths: >+ããããã«ã¬ã¼ >+<span font_feature_settings="hwid=1">ããããã«ã¬ã¼</span> >+ >+Standard Ligatures: >+<span font_desc="FreeSans 8"> >+<span font_feature_settings="liga=0">ffl fi</span> >+ffl fi >+</span> >+ >+Discretionary Ligatures: >+æ ªå¼ä¼ç¤¾ >+<span font_feature_settings="dlig=1">æ ªå¼ä¼ç¤¾</span> >+ >+Discretionary Ligatures (+ Vertical Alternates) >+æ ªå¼ä¼ç¤¾ >+<span font_feature_settings="vert=1,dlig=1">æ ªå¼ä¼ç¤¾</span> >+ >+Vertical Alternates: >+<span font_feature_settings="vert=1">ç§ã®ååã¯ä¸éã§ãã</span> >+ >+Fractions: >+<span font_desc="Linux Libertine O 8"> >+1/1000 >+<span font_feature_settings="frac=1">1/1000</span> >+</span> >+ >+Traditional Forms: >+äº >+<span font_feature_settings="trad=1">äº</span> >+ >+JIS X 0213:2000 >+<span font_feature_settings="jp90=1"> >+é¢è¦é£´æº¢è¨é°¯æ·«è¿å©åé¤è¥è¿¦çå»»æ¢æ¦è¹èééç¿°ç¿«å¾½ >+ç¥æ±²ç¸ç¬å¿é¥åå°æ«å±ç²ç¥éå¦æ²ç½éµè«ºå··æ¢èéµ çå >+æ¦è©é¯é鮫é¤æç¼é楯è¯è·å¨éæèè¨éæºæ°çç½ç©¿ç® >+è©®åé¡æéè¿è¸è¾¿æ¨½æ註ç¦ææ§éè¾»æºéæ¢æººåå µå± è³ >+çéè¬ç楢禰çé秤é§ç®¸åæ½èª¹æ¨ç¨é¼è¬¬è±¹å»çæ§è½ç¥ >+èç¯å¨©éåºè¬é±è¿å²é¤ç±¾çºéæç·æ¼£çç°¾æ¦å±¢å¤åå¬å² >+åå¾ææ£æ©ç¡çç¦ç¼ç¥ç«çµç¯è±èèèèè è¨éé±é¨é´ >+</span> >+ >+JIS X 0213:2004 >+<span font_feature_settings="jp04=1"> >+é¢è¦é£´æº¢è¨é°¯æ·«è¿å©åé¤è¥è¿¦çå»»æ¢æ¦è¹èééç¿°ç¿«å¾½ >+ç¥æ±²ç¸ç¬å¿é¥åå°æ«å±ç²ç¥éå¦æ²ç½éµè«ºå··æ¢èéµ çå >+æ¦è©é¯é鮫é¤æç¼é楯è¯è·å¨éæèè¨éæºæ°çç½ç©¿ç® >+è©®åé¡æéè¿è¸è¾¿æ¨½æ註ç¦ææ§éè¾»æºéæ¢æººåå µå± è³ >+çéè¬ç楢禰çé秤é§ç®¸åæ½èª¹æ¨ç¨é¼è¬¬è±¹å»çæ§è½ç¥ >+èç¯å¨©éåºè¬é±è¿å²é¤ç±¾çºéæç·æ¼£çç°¾æ¦å±¢å¤åå¬å² >+åå¾ææ£æ©ç¡çç¦ç¼ç¥ç«çµç¯è±èèèèè è¨éé±é¨é´ >+</span> >+ >+Alternate Annotation Forms: >+CR+12345ä¸äºä¸æç«æ°´æ¨éåæ¥æ ªæ社å >+<span font_feature_settings="nalt=1">CR+12345ä¸äºä¸æç«æ°´æ¨éåæ¥æ ªæ社å</span> >+ >+</span> >diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c >index 09dcbbd..4a69258 100644 >--- a/pango/pango-attributes.c >+++ b/pango/pango-attributes.c >@@ -1101,6 +1101,69 @@ pango_attr_gravity_hint_new (PangoGravityHint hint) > return pango_attr_int_new (&klass, (int)hint); > } > >+static PangoAttribute * >+pango_attr_feature_copy (const PangoAttribute *attr) >+{ >+ const PangoAttrFeature *fattr = (PangoAttrFeature *)attr; >+ >+ return pango_attr_feature_new (fattr->tag, >+ fattr->value); >+} >+ >+static void >+pango_attr_feature_destroy (PangoAttribute *attr) >+{ >+ PangoAttrFeature *fattr = (PangoAttrFeature *)attr; >+ >+ g_free (fattr->tag); >+ g_free (fattr->value); >+ g_slice_free (PangoAttrFeature, fattr); >+} >+ >+static gboolean >+pango_attr_feature_equal (const PangoAttribute *attr1, >+ const PangoAttribute *attr2) >+{ >+ const PangoAttrFeature *fattr1 = (const PangoAttrFeature *)attr1; >+ const PangoAttrFeature *fattr2 = (const PangoAttrFeature *)attr2; >+ gboolean f1 = g_ascii_strcasecmp (fattr1->value, "1") == 0 || >+ g_ascii_strcasecmp (fattr1->value, "true") == 0 || >+ g_ascii_strcasecmp (fattr1->value, "yes") == 0 ? TRUE : FALSE; >+ gboolean f2 = g_ascii_strcasecmp (fattr2->value, "1") == 0 || >+ g_ascii_strcasecmp (fattr2->value, "true") == 0 || >+ g_ascii_strcasecmp (fattr2->value, "yes") == 0 ? TRUE : FALSE; >+ >+ return g_ascii_strcasecmp (fattr1->tag, fattr2->tag) == 0 && f1 == f2; >+} >+ >+/** >+ * pango_attr_feature_new: >+ * @feature: a feature tag >+ * @value: a value to set for a feature tag >+ * >+ * Create a new feature tag attribute. >+ * >+ * Return value: (transfer full): the newly allocated #PangoAttribute, >+ * which should be freed with pango_attribute_destroy(). >+ **/ >+PangoAttribute * >+pango_attr_feature_new (const char *feature, >+ const char *value) >+{ >+ PangoAttrFeature *result = g_slice_new (PangoAttrFeature); >+ static const PangoAttrClass klass = { >+ PANGO_ATTR_FEATURE, >+ pango_attr_feature_copy, >+ pango_attr_feature_destroy, >+ pango_attr_feature_equal >+ }; >+ >+ pango_attribute_init (&result->attr, &klass); >+ result->tag = g_strdup (feature); >+ result->value = g_strdup (value); >+ >+ return (PangoAttribute *)result; >+} > > /* > * Attribute List >@@ -1931,7 +1994,8 @@ pango_attr_iterator_get_font (PangoAttrIterator *iterator, > while (tmp_list2) > { > PangoAttribute *old_attr = tmp_list2->data; >- if (attr->klass->type == old_attr->klass->type) >+ if (attr->klass->type == old_attr->klass->type && >+ attr->klass->type != PANGO_ATTR_FEATURE) > { > found = TRUE; > break; >diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h >index 1e0feb7..d373419 100644 >--- a/pango/pango-attributes.h >+++ b/pango/pango-attributes.h >@@ -75,6 +75,7 @@ typedef struct _PangoAttrFloat PangoAttrFloat; > typedef struct _PangoAttrColor PangoAttrColor; > typedef struct _PangoAttrFontDesc PangoAttrFontDesc; > typedef struct _PangoAttrShape PangoAttrShape; >+typedef struct _PangoAttrFeature PangoAttrFeature; > > /** > * PANGO_TYPE_ATTR_LIST: >@@ -167,7 +168,8 @@ typedef enum > PANGO_ATTR_STRIKETHROUGH_COLOR,/* PangoAttrColor */ > PANGO_ATTR_ABSOLUTE_SIZE, /* PangoAttrSize */ > PANGO_ATTR_GRAVITY, /* PangoAttrInt */ >- PANGO_ATTR_GRAVITY_HINT /* PangoAttrInt */ >+ PANGO_ATTR_GRAVITY_HINT, /* PangoAttrInt */ >+ PANGO_ATTR_FEATURE /* PangoAttrFeature */ > } PangoAttrType; > > /** >@@ -406,6 +408,22 @@ struct _PangoAttrFontDesc > PangoFontDescription *desc; > }; > >+/** >+ * PangoAttrFeature: >+ * @attr: the common portion of the attribute >+ * @tag: the feature tag for OpenType >+ * @value: the value to set for @tag >+ * >+ * The #PangoAttrFeature structure is used to represent attributes that >+ * is the feature tag in OpenType. >+ */ >+struct _PangoAttrFeature >+{ >+ PangoAttribute attr; >+ gchar *tag; >+ gchar *value; >+}; >+ > PangoAttrType pango_attr_type_register (const gchar *name); > const char * pango_attr_type_get_name (PangoAttrType type) G_GNUC_CONST; > >@@ -456,6 +474,8 @@ PangoAttribute *pango_attr_shape_new_with_data (const PangoRectangle *ink_ > > PangoAttribute *pango_attr_gravity_new (PangoGravity gravity); > PangoAttribute *pango_attr_gravity_hint_new (PangoGravityHint hint); >+PangoAttribute *pango_attr_feature_new (const char *feature, >+ const char *value); > > GType pango_attr_list_get_type (void) G_GNUC_CONST; > PangoAttrList * pango_attr_list_new (void); >diff --git a/pango/pango-markup.c b/pango/pango-markup.c >index c96c29e..acdee36 100644 >--- a/pango/pango-markup.c >+++ b/pango/pango-markup.c >@@ -1077,6 +1077,7 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, > const char *fallback = NULL; > const char *gravity = NULL; > const char *gravity_hint = NULL; >+ char **features = NULL; > > g_markup_parse_context_get_position (context, > &line_number, &char_number); >@@ -1090,6 +1091,13 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, > names[i], line_number, char_number); \ > return FALSE; \ > }}G_STMT_END >+#define CHECK_ATTRIBUTE_ARRAY2(var, name) \ >+ if (attr_strcmp (names[i], (name)) == 0) { \ >+ CHECK_DUPLICATE (var); \ >+ (var) = g_strsplit_set (values[i], " ,", -1); \ >+ found = TRUE; \ >+ break; \ >+ } > #define CHECK_ATTRIBUTE2(var, name) \ > if (attr_strcmp (names[i], (name)) == 0) { \ > CHECK_DUPLICATE (var); \ >@@ -1120,6 +1128,7 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, > > CHECK_ATTRIBUTE (foreground); > CHECK_ATTRIBUTE2 (foreground, "fgcolor"); >+ CHECK_ATTRIBUTE_ARRAY2 (features, "font_feature_settings"); > break; > case 's': > CHECK_ATTRIBUTE (size); >@@ -1430,6 +1439,19 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, > add_attribute (tag, > pango_attr_language_new (pango_language_from_string (lang))); > } >+ if (G_UNLIKELY (features)) >+ { >+ gint i; >+ >+ for (i = 0; features[i]; i++) >+ { >+ gchar **v = g_strsplit (features[i], "=", 2); >+ if (v[0]) >+ add_attribute (tag, pango_attr_feature_new (v[0], v[1])); >+ g_strfreev (v); >+ } >+ g_strfreev (features); >+ } > > return TRUE; > >-- >2.1.0 >
Created attachment 304721 [details] [review] Add support for OpenType features in attributes and markup This commit adds support for a font_features attribute in Pango markup, and a new PangoAttributeFontFeatures attribute to hold it. The syntax of the value is very similar to the font-feature-settings CSS3 property: a comma-separated list of font feature strings. The individual font features are parsed with hb_feature_from_string().
Version 4, against git master.
*** Bug 545510 has been marked as a duplicate of this bug. ***
Committed! Thanks everyone! commit ba53f29f7c2c105becb898d6417a4c160b7fc1e5 Author: Behdad Esfahbod <behdad@behdad.org> Date: Thu Jun 18 11:49:40 2015 -0700 Bug 738505 - Add fontfeatures support in PangoAttributes and markup https://bugzilla.gnome.org/show_bug.cgi?id=738505 Patch from Matthias Clasen, based on early patch from Akira TAGOH. There's room for improvement in how this is done, but it works now for simple cases, which is what most people will be using it for. Finally!
The test-feature-tag.markup file is missing.
Pushed. Thanks.
I think you should also create a page to illustrate the usage of OpenType feature in Pango, you can create this page through a grey link that can be found in this page: http://www.pango.org/ScriptGallery