| <?xml version="1.0"?> |
| <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" |
| "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [ |
| <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'"> |
| <!ENTITY version SYSTEM "version.xml"> |
| ]> |
| <chapter id="shaping-and-shape-plans"> |
| <title>Shaping and shape plans</title> |
| <para> |
| Once you have your face and font objects configured as desired and |
| your input buffer is filled with the characters you need to shape, |
| all you need to do is call <function>hb_shape()</function>. |
| </para> |
| <para> |
| HarfBuzz will return the shaped version of the text in the same |
| buffer that you provided, but it will be in output mode. At that |
| point, you can iterate through the glyphs in the buffer, drawing |
| each one at the specified position or handing them off to the |
| appropriate graphics library. |
| </para> |
| <para> |
| For the most part, HarfBuzz's shaping step is straightforward from |
| the outside. But that doesn't mean there will never be cases where |
| you want to look under the hood and see what is happening on the |
| inside. HarfBuzz provides facilities for doing that, too. |
| </para> |
| |
| <section id="shaping-buffer-output"> |
| <title>Shaping and buffer output</title> |
| <para> |
| The <function>hb_shape()</function> function call takes four arguments: the font |
| object to use, the buffer of characters to shape, an array of |
| user-specified features to apply, and the length of that feature |
| array. The feature array can be NULL, so for the sake of |
| simplicity we will start with that case. |
| </para> |
| <para> |
| Internally, HarfBuzz looks at the tables of the font file to |
| determine where glyph classes, substitutions, and positioning |
| are defined, using that information to decide which |
| <emphasis>shaper</emphasis> to use (<literal>ot</literal> for |
| OpenType fonts, <literal>aat</literal> for Apple Advanced |
| Typography fonts, and so on). It also looks at the direction, |
| script, and language properties of the segment to figure out |
| which script-specific shaping model is needed (at least, in |
| shapers that support multiple options). |
| </para> |
| <para> |
| If a font has a GDEF table, then that is used for |
| glyph classes; if not, HarfBuzz will fall back to Unicode |
| categorization by code point. If a font has an AAT <literal>morx</literal> table, |
| then it is used for substitutions; if not, but there is a GSUB |
| table, then the GSUB table is used. If the font has an AAT |
| <literal>kerx</literal> table, then it is used for positioning; if not, but |
| there is a GPOS table, then the GPOS table is used. If neither |
| table is found, but there is a <literal>kern</literal> table, then HarfBuzz will |
| use the <literal>kern</literal> table. If there is no <literal>kerx</literal>, no GPOS, and no |
| <literal>kern</literal>, HarfBuzz will fall back to positioning marks itself. |
| </para> |
| <para> |
| With a well-behaved OpenType font, you expect GDEF, GSUB, and |
| GPOS tables to all be applied. HarfBuzz implements the |
| script-specific shaping models in internal functions, rather |
| than in the public API. |
| </para> |
| <para> |
| The algorithms |
| used for shaping can be quite involved; HarfBuzz tries |
| to be compatible with the OpenType Layout specification |
| and, wherever there is any ambiguity, HarfBuzz attempts to replicate the |
| output of Microsoft's Uniscribe engine, to the extent that is feasible and desirable. See the <ulink |
| url="https://docs.microsoft.com/en-us/typography/script-development/standard">Microsoft |
| Typography pages</ulink> for more detail. |
| </para> |
| <para> |
| In general, though, all that you need to know is that |
| <function>hb_shape()</function> returns the results of shaping |
| in the same buffer that you provided. The buffer's content type |
| will now be set to |
| <literal>HB_BUFFER_CONTENT_TYPE_GLYPHS</literal>, indicating |
| that it contains shaped output, rather than input text. You can |
| now extract the glyph information and positioning arrays: |
| </para> |
| <programlisting language="C"> |
| hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count); |
| hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count); |
| </programlisting> |
| <para> |
| The glyph information array holds a <type>hb_glyph_info_t</type> |
| for each output glyph, which has two fields: |
| <parameter>codepoint</parameter> and |
| <parameter>cluster</parameter>. Whereas, in the input buffer, |
| the <parameter>codepoint</parameter> field contained the Unicode |
| code point, it now contains the glyph ID of the corresponding |
| glyph in the font. The <parameter>cluster</parameter> field is |
| an integer that you can use to help identify when shaping has |
| reordered, split, or combined code points; we will say more |
| about that in the next chapter. |
| </para> |
| <para> |
| The glyph positions array holds a corresponding |
| <type>hb_glyph_position_t</type> for each output glyph, |
| containing four fields: <parameter>x_advance</parameter>, |
| <parameter>y_advance</parameter>, |
| <parameter>x_offset</parameter>, and |
| <parameter>y_offset</parameter>. The advances tell you how far |
| you need to move the drawing point after drawing this glyph, |
| depending on whether you are setting horizontal text (in which |
| case you will have x advances) or vertical text (for which you |
| will have y advances). The x and y offsets tell you where to |
| move to start drawing the glyph; usually you will have both and |
| x and a y offset, regardless of the text direction. |
| </para> |
| <para> |
| Most of the time, you will rely on a font-rendering library or |
| other graphics library to do the actual drawing of glyphs, so |
| you will need to iterate through the glyphs in the buffer and |
| pass the corresponding values off. |
| </para> |
| </section> |
| |
| <section id="shaping-opentype-features"> |
| <title>OpenType features</title> |
| <para> |
| OpenType features enable fonts to include smart behavior, |
| implemented as "lookup" rules stored in the GSUB and GPOS |
| tables. The OpenType specification defines a long list of |
| standard features that fonts can use for these behaviors; each |
| feature has a four-character reserved name and a well-defined |
| semantic meaning. |
| </para> |
| <para> |
| Some OpenType features are defined for the purpose of supporting |
| script-specific shaping, and are automatically activated, but |
| only when a buffer's script property is set to a script that the |
| feature supports. |
| </para> |
| <para> |
| Other features are more generic and can apply to several (or |
| any) script, and shaping engines are expected to implement |
| them. By default, HarfBuzz activates several of these features |
| on every text run. They include <literal>abvm</literal>, |
| <literal>blwm</literal>, <literal>ccmp</literal>, |
| <literal>locl</literal>, <literal>mark</literal>, |
| <literal>mkmk</literal>, and <literal>rlig</literal>. |
| </para> |
| <para> |
| In addition, if the text direction is horizontal, HarfBuzz |
| also applies the <literal>calt</literal>, |
| <literal>clig</literal>, <literal>curs</literal>, |
| <literal>dist</literal>, <literal>kern</literal>, |
| <literal>liga</literal> and <literal>rclt</literal>, features. |
| </para> |
| <para> |
| Additionally, when HarfBuzz encounters a fraction slash |
| (<literal>U+2044</literal>), it looks backward and forward for decimal |
| digits (Unicode General Category = Nd), and enables features |
| <literal>numr</literal> on the sequence before the fraction slash, |
| <literal>dnom</literal> on the sequence after the fraction slash, |
| and <literal>frac</literal> on the whole sequence including the fraction |
| slash. |
| </para> |
| <para> |
| Some script-specific shaping models |
| (see <xref linkend="opentype-shaping-models" />) disable some of the |
| features listed above: |
| </para> |
| <itemizedlist> |
| <listitem> |
| <para> |
| Hangul: <literal>calt</literal> |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Indic: <literal>liga</literal> |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Khmer: <literal>liga</literal> |
| </para> |
| </listitem> |
| </itemizedlist> |
| <para> |
| If the text direction is vertical, HarfBuzz applies |
| the <literal>vert</literal> feature by default. |
| </para> |
| <para> |
| Still other features are designed to be purely optional and left |
| up to the application or the end user to enable or disable as desired. |
| </para> |
| <para> |
| You can adjust the set of features that HarfBuzz applies to a |
| buffer by supplying an array of <type>hb_feature_t</type> |
| features as the third argument to |
| <function>hb_shape()</function>. For a simple case, let's just |
| enable the <literal>dlig</literal> feature, which turns on any |
| "discretionary" ligatures in the font: |
| </para> |
| <programlisting language="C"> |
| hb_feature_t userfeatures[1]; |
| userfeatures[0].tag = HB_TAG('d','l','i','g'); |
| userfeatures[0].value = 1; |
| userfeatures[0].start = HB_FEATURE_GLOBAL_START; |
| userfeatures[0].end = HB_FEATURE_GLOBAL_END; |
| </programlisting> |
| <para> |
| <literal>HB_FEATURE_GLOBAL_END</literal> and |
| <literal>HB_FEATURE_GLOBAL_END</literal> are macros we can use |
| to indicate that the features will be applied to the entire |
| buffer. We could also have used a literal <literal>0</literal> |
| for the start and a <literal>-1</literal> to indicate the end of |
| the buffer (or have selected other start and end positions, if needed). |
| </para> |
| <para> |
| When we pass the <varname>userfeatures</varname> array to |
| <function>hb_shape()</function>, any discretionary ligature |
| substitutions from our font that match the text in our buffer |
| will get performed: |
| </para> |
| <programlisting language="C"> |
| hb_shape(font, buf, userfeatures, num_features); |
| </programlisting> |
| <para> |
| Just like we enabled the <literal>dlig</literal> feature by |
| setting its <parameter>value</parameter> to |
| <literal>1</literal>, you would disable a feature by setting its |
| <parameter>value</parameter> to <literal>0</literal>. Some |
| features can take other <parameter>value</parameter> settings; |
| be sure you read the full specification of each feature tag to |
| understand what it does and how to control it. |
| </para> |
| </section> |
| |
| <section id="shaping-shaper-selection"> |
| <title>Shaper selection</title> |
| <para> |
| The basic version of <function>hb_shape()</function> determines |
| its shaping strategy based on examining the capabilities of the |
| font file. OpenType font tables cause HarfBuzz to try the |
| <literal>ot</literal> shaper, while AAT font tables cause HarfBuzz to try the |
| <literal>aat</literal> shaper. |
| </para> |
| <para> |
| In the real world, however, a font might include some unusual |
| mix of tables, or one of the tables might simply be broken for |
| the script you need to shape. So, sometimes, you might not |
| want to rely on HarfBuzz's process for deciding what to do, and |
| just tell <function>hb_shape()</function> what you want it to try. |
| </para> |
| <para> |
| <function>hb_shape_full()</function> is an alternate shaping |
| function that lets you supply a list of shapers for HarfBuzz to |
| try, in order, when shaping your buffer. For example, if you |
| have determined that HarfBuzz's attempts to work around broken |
| tables gives you better results than the AAT shaper itself does, |
| you might move the AAT shaper to the end of your list of |
| preferences and call <function>hb_shape_full()</function> |
| </para> |
| <programlisting language="C"> |
| char *shaperprefs[3] = {"ot", "default", "aat"}; |
| ... |
| hb_shape_full(font, buf, userfeatures, num_features, shaperprefs); |
| </programlisting> |
| <para> |
| to get results you are happier with. |
| </para> |
| <para> |
| You may also want to call |
| <function>hb_shape_list_shapers()</function> to get a list of |
| the shapers that were built at compile time in your copy of HarfBuzz. |
| </para> |
| </section> |
| |
| <section id="shaping-plans-and-caching"> |
| <title>Plans and caching</title> |
| <para> |
| Internally, HarfBuzz uses a structure called a shape plan to |
| track its decisions about how to shape the contents of a |
| buffer. The <function>hb_shape()</function> function builds up the shape plan by |
| examining segment properties and by inspecting the contents of |
| the font. |
| </para> |
| <para> |
| This process can involve some decision-making and |
| trade-offs — for example, HarfBuzz inspects the GSUB and GPOS |
| lookups for the script and language tags set on the segment |
| properties, but it falls back on the lookups under the |
| <literal>DFLT</literal> tag (and sometimes other common tags) |
| if there are actually no lookups for the tag requested. |
| </para> |
| <para> |
| HarfBuzz also includes some work-arounds for |
| handling well-known older font conventions that do not follow |
| OpenType or Unicode specifications, for buggy system fonts, and for |
| peculiarities of Microsoft Uniscribe. All of that means that a |
| shape plan, while not something that you should edit directly in |
| client code, still might be an object that you want to |
| inspect. Furthermore, if resources are tight, you might want to |
| cache the shape plan that HarfBuzz builds for your buffer and |
| font, so that you do not have to rebuild it for every shaping call. |
| </para> |
| <para> |
| You can create a cacheable shape plan with |
| <function>hb_shape_plan_create_cached(face, props, |
| user_features, num_user_features, shaper_list)</function>, where |
| <parameter>face</parameter> is a face object (not a font object, |
| notably), <parameter>props</parameter> is an |
| <type>hb_segment_properties_t</type>, |
| <parameter>user_features</parameter> is an array of |
| <type>hb_feature_t</type>s (with length |
| <parameter>num_user_features</parameter>), and |
| <parameter>shaper_list</parameter> is a list of shapers to try. |
| </para> |
| <para> |
| Shape plans are objects in HarfBuzz, so there are |
| reference-counting functions and user-data attachment functions |
| you can |
| use. <function>hb_shape_plan_reference(shape_plan)</function> |
| increases the reference count on a shape plan, while |
| <function>hb_shape_plan_destroy(shape_plan)</function> decreases |
| the reference count, destroying the shape plan when the last |
| reference is dropped. |
| </para> |
| <para> |
| You can attach user data to a shaper (with a key) using the |
| <function>hb_shape_plan_set_user_data(shape_plan,key,data,destroy,replace)</function> |
| function, optionally supplying a <function>destroy</function> |
| callback to use. You can then fetch the user data attached to a |
| shape plan with |
| <function>hb_shape_plan_get_user_data(shape_plan, key)</function>. |
| </para> |
| </section> |
| |
| </chapter> |