Simon Cozens | 11a07c4 | 2015-08-31 10:39:10 +0100 | [diff] [blame] | 1 | <chapter id="hello-harfbuzz"> |
Simon Cozens | f080765 | 2015-08-25 19:57:15 +0100 | [diff] [blame] | 2 | <title>Hello, Harfbuzz</title> |
| 3 | <para> |
| 4 | Here's the simplest Harfbuzz that can possibly work. We will improve |
| 5 | it later. |
| 6 | </para> |
| 7 | <orderedlist numeration="arabic"> |
| 8 | <listitem> |
| 9 | <para> |
| 10 | Create a buffer and put your text in it. |
| 11 | </para> |
| 12 | </listitem> |
| 13 | </orderedlist> |
| 14 | <programlisting language="C"> |
| 15 | #include <hb.h> |
| 16 | hb_buffer_t *buf; |
| 17 | buf = hb_buffer_create(); |
| 18 | hb_buffer_add_utf8(buf, text, strlen(text), 0, strlen(text)); |
| 19 | </programlisting> |
| 20 | <orderedlist numeration="arabic"> |
| 21 | <listitem override="2"> |
| 22 | <para> |
| 23 | Guess the script, language and direction of the buffer. |
| 24 | </para> |
| 25 | </listitem> |
| 26 | </orderedlist> |
| 27 | <programlisting language="C"> |
| 28 | hb_buffer_guess_segment_properties(buf); |
| 29 | </programlisting> |
| 30 | <orderedlist numeration="arabic"> |
| 31 | <listitem override="3"> |
| 32 | <para> |
| 33 | Create a face and a font, using FreeType for now. |
| 34 | </para> |
| 35 | </listitem> |
| 36 | </orderedlist> |
| 37 | <programlisting language="C"> |
| 38 | #include <hb-ft.h> |
| 39 | FT_New_Face(ft_library, font_path, index, &face) |
| 40 | hb_font_t *font = hb_ft_font_create(face); |
| 41 | </programlisting> |
| 42 | <orderedlist numeration="arabic"> |
| 43 | <listitem override="4"> |
| 44 | <para> |
| 45 | Shape! |
| 46 | </para> |
| 47 | </listitem> |
| 48 | </orderedlist> |
| 49 | <programlisting> |
| 50 | hb_shape(font, buf, NULL, 0); |
| 51 | </programlisting> |
| 52 | <orderedlist numeration="arabic"> |
| 53 | <listitem override="5"> |
| 54 | <para> |
| 55 | Get the glyph and position information. |
| 56 | </para> |
| 57 | </listitem> |
| 58 | </orderedlist> |
| 59 | <programlisting language="C"> |
| 60 | hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count); |
| 61 | hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count); |
| 62 | </programlisting> |
| 63 | <orderedlist numeration="arabic"> |
| 64 | <listitem override="6"> |
| 65 | <para> |
| 66 | Iterate over each glyph. |
| 67 | </para> |
| 68 | </listitem> |
| 69 | </orderedlist> |
| 70 | <programlisting language="C"> |
| 71 | for (i = 0; i < glyph_count; ++i) { |
| 72 | glyphid = glyph_info[i].codepoint; |
| 73 | x_offset = glyph_pos[i].x_offset / 64.0; |
| 74 | y_offset = glyph_pos[i].y_offset / 64.0; |
| 75 | x_advance = glyph_pos[i].x_advance / 64.0; |
| 76 | y_advance = glyph_pos[i].y_advance / 64.0; |
| 77 | draw_glyph(glyphid, cursor_x + x_offset, cursor_y + y_offset); |
| 78 | cursor_x += x_advance; |
| 79 | cursor_y += y_advance; |
| 80 | } |
| 81 | </programlisting> |
| 82 | <orderedlist numeration="arabic"> |
| 83 | <listitem override="7"> |
| 84 | <para> |
| 85 | Tidy up. |
| 86 | </para> |
| 87 | </listitem> |
| 88 | </orderedlist> |
| 89 | <programlisting language="C"> |
| 90 | hb_buffer_destroy(buf); |
| 91 | hb_font_destroy(hb_ft_font); |
| 92 | </programlisting> |
Simon Cozens | 11a07c4 | 2015-08-31 10:39:10 +0100 | [diff] [blame] | 93 | <section id="what-harfbuzz-doesnt-do"> |
Simon Cozens | f080765 | 2015-08-25 19:57:15 +0100 | [diff] [blame] | 94 | <title>What Harfbuzz doesn't do</title> |
| 95 | <para> |
| 96 | The code above will take a UTF8 string, shape it, and give you the |
| 97 | information required to lay it out correctly on a single |
| 98 | horizontal (or vertical) line using the font provided. That is the |
| 99 | extent of Harfbuzz's responsibility. |
| 100 | </para> |
| 101 | <para> |
| 102 | If you are implementing a text layout engine you may have other |
| 103 | responsibilities, that Harfbuzz will not help you with: |
| 104 | </para> |
| 105 | <itemizedlist> |
| 106 | <listitem> |
| 107 | <para> |
| 108 | Harfbuzz won't help you with bidirectionality. If you want to |
| 109 | lay out text with mixed Hebrew and English, you will need to |
| 110 | ensure that the buffer provided to Harfbuzz has those |
| 111 | characters in the correct layout order. This will be different |
| 112 | from the logical order in which the Unicode text is stored. In |
| 113 | other words, the user will hit the keys in the following |
| 114 | sequence: |
| 115 | </para> |
| 116 | <programlisting> |
| 117 | A B C [space] ג ב א [space] D E F |
| 118 | </programlisting> |
| 119 | <para> |
| 120 | but will expect to see in the output: |
| 121 | </para> |
| 122 | <programlisting> |
| 123 | ABC אבג DEF |
| 124 | </programlisting> |
| 125 | <para> |
| 126 | This reordering is called <emphasis>bidi processing</emphasis> |
| 127 | ("bidi" is short for bidirectional), and there's an |
| 128 | algorithm as an annex to the Unicode Standard which tells you how |
| 129 | to reorder a string from logical order into presentation order. |
| 130 | Before sending your string to Harfbuzz, you may need to apply the |
| 131 | bidi algorithm to it. Libraries such as ICU and fribidi can do |
| 132 | this for you. |
| 133 | </para> |
Simon Cozens | 387d6af | 2015-08-31 10:31:09 +0100 | [diff] [blame] | 134 | </listitem> |
Simon Cozens | f080765 | 2015-08-25 19:57:15 +0100 | [diff] [blame] | 135 | <listitem> |
| 136 | <para> |
| 137 | Harfbuzz won't help you with text that contains different font |
| 138 | properties. For instance, if you have the string "a |
| 139 | <emphasis>huge</emphasis> breakfast", and you expect |
| 140 | "huge" to be italic, you will need to send three |
| 141 | strings to Harfbuzz: <literal>a</literal>, in your Roman font; |
| 142 | <literal>huge</literal> using your italic font; and |
| 143 | <literal>breakfast</literal> using your Roman font again. |
| 144 | Similarly if you change font, font size, script, language or |
| 145 | direction within your string, you will need to shape each run |
| 146 | independently and then output them independently. Harfbuzz |
| 147 | expects to shape a run of characters sharing the same |
| 148 | properties. |
| 149 | </para> |
| 150 | </listitem> |
| 151 | <listitem> |
| 152 | <para> |
| 153 | Harfbuzz won't help you with line breaking, hyphenation or |
| 154 | justification. As mentioned above, it lays out the string |
| 155 | along a <emphasis>single line</emphasis> of, notionally, |
| 156 | infinite length. If you want to find out where the potential |
| 157 | word, sentence and line break points are in your text, you |
| 158 | could use the ICU library's break iterator functions. |
| 159 | </para> |
| 160 | <para> |
| 161 | Harfbuzz can tell you how wide a shaped piece of text is, which is |
| 162 | useful input to a justification algorithm, but it knows nothing |
| 163 | about paragraphs, lines or line lengths. Nor will it adjust the |
| 164 | space between words to fit them proportionally into a line. If you |
| 165 | want to layout text in paragraphs, you will probably want to send |
| 166 | each word of your text to Harfbuzz to determine its shaped width |
| 167 | after glyph substitutions, then work out how many words will fit |
| 168 | on a line, and then finally output each word of the line separated |
| 169 | by a space of the correct size to fully justify the paragraph. |
| 170 | </para> |
| 171 | </listitem> |
| 172 | </itemizedlist> |
| 173 | <para> |
| 174 | As a layout engine implementor, Harfbuzz will help you with the |
| 175 | interface between your text and your font, and that's something |
| 176 | that you'll need - what you then do with the glyphs that your font |
| 177 | returns is up to you. The example we saw above enough to get us |
| 178 | started using Harfbuzz. Now we are going to use the remainder of |
| 179 | Harfbuzz's API to refine that example and improve our text shaping |
| 180 | capabilities. |
| 181 | </para> |
Simon Cozens | 11a07c4 | 2015-08-31 10:39:10 +0100 | [diff] [blame] | 182 | </section> |
| 183 | </chapter> |