|
|
|
<chapter id="getting-started">
|
|
|
|
<title>Getting started with HarfBuzz</title>
|
|
|
|
<section>
|
|
|
|
<title>An overview of the HarfBuzz shaping API</title>
|
|
|
|
<para>
|
|
|
|
The core of the HarfBuzz shaping API is the function
|
|
|
|
<function>hb_shape()</function>. This function takes a font, a
|
|
|
|
buffer containing a string of Unicode codepoints and
|
|
|
|
(optionally) a list of font features as its input. It replaces
|
|
|
|
the codepoints in the buffer with the corresponding glyphs from
|
|
|
|
the font, correctly ordered and positioned, and with any of the
|
|
|
|
optional font features applied.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
In addition to holding the pre-shaping input (the Unicode
|
|
|
|
codepoints that comprise the input string) and the post-shaping
|
|
|
|
output (the glyphs and positions), a HarfBuzz buffer has several
|
|
|
|
properties that affect shaping. The most important are the
|
|
|
|
text-flow direction (e.g., left-to-right, right-to-left,
|
|
|
|
top-to-bottom, or bottom-to-top), the script tag, and the
|
|
|
|
language tag. HarfBuzz can attempt to guess the correct values
|
|
|
|
for the buffer based on its contents if you do not set them
|
|
|
|
explicitly.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
For input string buffers, flags are available to denote when the
|
|
|
|
buffer represents the beginning or end of a paragraph, to
|
|
|
|
indicate whether or not to visibly render Unicode <literal>Default
|
|
|
|
Ignorable</literal> codepoints, and to modify the cluster-merging
|
|
|
|
behavior for the buffer. For shaped output buffers, the
|
|
|
|
individual X and Y offsets and widths of each glyph are
|
|
|
|
accessible. HarfBuzz also flags glyphs as
|
|
|
|
<literal>UNSAFE_TO_BREAK</literal> if breaking the string at
|
|
|
|
that glyph (e.g., in a line-breaking or hyphenation process)
|
|
|
|
would alter the shaping output for the buffer.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
HarfBuzz also provides methods to compare the contents of
|
|
|
|
buffers, join buffers, normalize buffer contents, and handle
|
|
|
|
invalid codepoints, as well as to determine the state of a
|
|
|
|
buffer (e.g., input codepoints or output glyphs). Buffer
|
|
|
|
lifecycles are managed and all buffers are reference-counted.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Although the default <function>hb_shape()</function> function is
|
|
|
|
sufficient for most use cases, a variant is also provide that
|
|
|
|
lets you specify which of HarfBuzz's shapers to use on a buffer.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
HarfBuzz can read TrueType fonts, TrueType collections, OpenType
|
|
|
|
fonts, and OpenType collections. Functions are provided to query
|
|
|
|
font objects about metrics, Unicode coverage, available tables and
|
|
|
|
features, and variation selectors. Individual glyphs can also be
|
|
|
|
queried for metrics, variations, and glyph names. OpenType
|
|
|
|
variable fonts are supported, and HarfBuzz allows you to set
|
|
|
|
variation-axis coordinates on font objects.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
HarfBuzz provides glue code to integrate with FreeType, GObject,
|
|
|
|
Uniscribe, and CoreText. Support for integrating with
|
|
|
|
DirectWrite is experimental at present.
|
|
|
|
</para>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Terminology</title>
|
|
|
|
<variablelist>
|
|
|
|
<varlistentry>
|
|
|
|
<term>shaper</term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
In HarfBuzz, a <emphasis>shaper</emphasis> is a
|
|
|
|
handler for a specific script shaping model. HarfBuzz
|
|
|
|
implements separate shapers for Indic, Arabic, Thai and
|
|
|
|
Lao, Khmer, Myanmar, Tibetan, Hangul, Hebrew, the
|
|
|
|
Universal Shaping Engine (USE), and a default shaper for
|
|
|
|
non-complex scripts.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term>cluster</term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
In text shaping, a <emphasis>cluster</emphasis> is a
|
|
|
|
sequence of codepoints that must be handled as an
|
|
|
|
indivisible unit. Clusters can include codepoint
|
|
|
|
sequences that form a ligature or base-and-mark
|
|
|
|
sequences. Tracking and preserving clusters is important
|
|
|
|
when shaping operations might separate or reorder
|
|
|
|
codepoints.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
HarfBuzz provides three cluster
|
|
|
|
<emphasis>levels</emphasis> that implement different
|
|
|
|
approaches to the problem of preserving clusters during
|
|
|
|
shaping operations.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
|
|
</variablelist>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>A simple shaping example</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Below is the simplest HarfBuzz shaping example possible.
|
|
|
|
</para>
|
|
|
|
<orderedlist numeration="arabic">
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Create a buffer and put your text in it.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
|
|
<programlisting language="C">
|
|
|
|
#include <hb.h>
|
|
|
|
hb_buffer_t *buf;
|
|
|
|
buf = hb_buffer_create();
|
|
|
|
hb_buffer_add_utf8(buf, text, -1, 0, -1);
|
|
|
|
</programlisting>
|
|
|
|
<orderedlist numeration="arabic">
|
|
|
|
<listitem override="2">
|
|
|
|
<para>
|
|
|
|
Guess the script, language and direction of the buffer.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
|
|
<programlisting language="C">
|
|
|
|
hb_buffer_set_direction(buf, HB_DIRECTION_LTR);
|
|
|
|
hb_buffer_set_script(buf, HB_SCRIPT_LATIN);
|
|
|
|
hb_buffer_set_language(buf, hb_language_from_string("en", -1));
|
|
|
|
</programlisting>
|
|
|
|
<orderedlist numeration="arabic">
|
|
|
|
<listitem override="3">
|
|
|
|
<para>
|
|
|
|
Create a face and a font, using FreeType for now.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
|
|
<programlisting language="C">
|
|
|
|
#include <hb-ft.h>
|
|
|
|
FT_New_Face(ft_library, font_path, index, &face)
|
|
|
|
hb_font_t *font = hb_ft_font_create(face);
|
|
|
|
</programlisting>
|
|
|
|
<orderedlist numeration="arabic">
|
|
|
|
<listitem override="4">
|
|
|
|
<para>
|
|
|
|
Shape!
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
|
|
<programlisting>
|
|
|
|
hb_shape(font, buf, NULL, 0);
|
|
|
|
</programlisting>
|
|
|
|
<orderedlist numeration="arabic">
|
|
|
|
<listitem override="5">
|
|
|
|
<para>
|
|
|
|
Get the glyph and position information.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
|
|
<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>
|
|
|
|
<orderedlist numeration="arabic">
|
|
|
|
<listitem override="6">
|
|
|
|
<para>
|
|
|
|
Iterate over each glyph.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
|
|
<programlisting language="C">
|
|
|
|
for (i = 0; i < glyph_count; ++i) {
|
|
|
|
glyphid = glyph_info[i].codepoint;
|
|
|
|
x_offset = glyph_pos[i].x_offset / 64.0;
|
|
|
|
y_offset = glyph_pos[i].y_offset / 64.0;
|
|
|
|
x_advance = glyph_pos[i].x_advance / 64.0;
|
|
|
|
y_advance = glyph_pos[i].y_advance / 64.0;
|
|
|
|
draw_glyph(glyphid, cursor_x + x_offset, cursor_y + y_offset);
|
|
|
|
cursor_x += x_advance;
|
|
|
|
cursor_y += y_advance;
|
|
|
|
}
|
|
|
|
</programlisting>
|
|
|
|
<orderedlist numeration="arabic">
|
|
|
|
<listitem override="7">
|
|
|
|
<para>
|
|
|
|
Tidy up.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
|
|
<programlisting language="C">
|
|
|
|
hb_buffer_destroy(buf);
|
|
|
|
hb_font_destroy(hb_ft_font);
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
This example shows enough to get us started using HarfBuzz. In
|
|
|
|
the sections that follow, we will use the remainder of
|
|
|
|
HarfBuzz's API to refine and extend the example and improve its
|
|
|
|
text-shaping capabilities.
|
|
|
|
</para>
|
|
|
|
</section>
|
|
|
|
</chapter>
|