[HarfBuzz] collect_glyphs() API

Jonathan Kew jfkthame at googlemail.com
Mon Dec 31 09:33:24 PST 2012


On 4/12/12 22:21, Behdad Esfahbod wrote:
> Hi Werner and Jonathan,
>
> Just wanted to follow up and bring the new
> hb_ot_layout_lookup_collect_glyphs() to your attention.
>
> The implementation of this API is complete as far as I'm concerned.  HOWEVER,
> I've done absolutely zero testing.

I've finally gotten around to experimenting with this in Firefox, and 
run into a couple of issues that mean it doesn't quite work as-is:

(1) hb_ot_layout_collect_lookups(), called without a specific language 
tag, fails to take account of the DefaultLangSys in the script 
record(s), which means it misses some lookups -- in the (common) case 
where the script has ONLY a DefaultLangSys, it returns no lookups at all.

A possible fix is to explicitly collect lookups for the DefaultLangSys 
in addition to any specified languages.

(In _hb_ot_layout_collect_lookups_languages:)

     /* All languages... */
     unsigned int count = hb_ot_layout_script_get_language_tags (face, 
table_tag, script_index, 0, NULL, NULL);
     for (unsigned int language_index = 0; language_index < count; 
language_index++)
       _hb_ot_layout_collect_lookups_features (face, table_tag, 
script_index, language_index, features, lookup_indexes);
     /* ...including the DefaultLangSys, which is not in the array of 
LangSysRecords */
     _hb_ot_layout_collect_lookups_features (face, table_tag, 
script_index, OT::Index::NOT_FOUND_INDEX, features, lookup_indexes);

(2) The API claims that you can pass NULL for any of the glyph sets. 
Internally, it handles this by using hb_set_get_empty() in place of any 
NULL parameters. However, this breaks because the low-level 
collect_glyphs() functions will then be trying to add glyphs to the 
(static, const) empty set. Ouch. For me, it dies with a protection error 
because that object is read-only, and const_casting it to a non-const 
hb_set* doesn't magically make it writable. But even if it didn't die, 
it would still be bad because it would make the "empty" object no longer 
empty!

This applies not only to any NULL glyph-set parameters that the user has 
passed, but also the use of hb_set_get_empty() in the 
hb_collect_glyphs_context_t::recurse() function to replace the user's 
before/input/after sets while recursing.

This could be fixed at various levels: the hb_set methods that modify a 
set could be made no-ops if the set is hb_set_get_empty(), though this 
adds an extra test to all those low-level operations; or -- better, I 
think -- the collect_glyphs() methods could check for (and skip) NULL 
sets, so that we don't need to use the const "empty" set for this purpose.

(To avoid the risk of errors like this, maybe hb_set_get_empty should be 
returning a const set?)

> That has to wait until I have time to do a
> cmdline tool for all the introspection APIs.  As such, thought I give you a
> heads up so you can go ahead and test, err, use it.
>
> Jonathan, I believe you know how to use the API.
>
> Werner, here's how you use the API:
>
>    1. Create a new set, call lookups (hb_set_create ()).
>
>    2. Collect the index of the lookups you want to process into the set.
>
>    3. Create sets for "backtrack", "input", "lookahead", and "output" glyph
> sets.  They can point to the same object, or some can be NULL.
>
>    4. Iterate over the lookup indices, and call collect_glyphs() for each.
>
> Note that if you reuse a set, you are responsible for emptying it first.
>
> Here is how one iterates over the set:
>
>    hb_codepoint_t lookup_index;
>    for (lookup_index = -1; hb_set_next (lookups, &lookup_index);)
>        hb_ot_layout_lookup_collect_glyphs (face, HB_OT_TAG_GSUB, lookup_index,
> ...);
>
> I probably should change the set API to use unsigned int instead of
> hb_codepoint_t, but I'm not going to worry about it that much right now.
>
> As for step 2, collecting lookup indices, there are different things you can
> do.  Based on what you described to me before, I think using
> hb_ot_layout_collect_lookups() makes most sense for you.  Say, if you want the
> 'scmp' feature for all scripts and languages, you call:
>
>    hb_tag_t features[] = {HB_TAG ('s','c','m','p'), HB_TAG_NONE};
>    hb_ot_layout_collect_lookups (face, HB_OT_TAG_GSUB, NULL, NULL, features,
> lookups);
>
> This will collect all the lookup indices for all scmp features in all scripts
> / language systems into the lookups set.  You can of course limit this to
> specific scripts, etc.
>
> Hope that helps,
>




More information about the HarfBuzz mailing list