usermanual-clusters.xml 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. <?xml version="1.0"?>
  2. <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
  3. "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
  4. <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
  5. <!ENTITY version SYSTEM "version.xml">
  6. ]>
  7. <chapter id="clusters">
  8. <title>Clusters</title>
  9. <section id="clusters-and-shaping">
  10. <title>Clusters and shaping</title>
  11. <para>
  12. In text shaping, a <emphasis>cluster</emphasis> is a sequence of
  13. characters that needs to be treated as a single, indivisible
  14. unit. A single letter or symbol can be a cluster of its
  15. own. Other clusters correspond to longer subsequences of the
  16. input code points &mdash; such as a ligature or conjunct form
  17. &mdash; and require the shaper to ensure that the cluster is not
  18. broken during the shaping process.
  19. </para>
  20. <para>
  21. A cluster is distinct from a <emphasis>grapheme</emphasis>,
  22. which is the smallest unit of meaning in a writing system or
  23. script.
  24. </para>
  25. <para>
  26. The definitions of the two terms are similar. However, clusters
  27. are only relevant for script shaping and glyph layout. In
  28. contrast, graphemes are a property of the underlying script, and
  29. are of interest when client programs implement orthographic
  30. or linguistic functionality.
  31. </para>
  32. <para>
  33. For example, two individual letters are often two separate
  34. graphemes. When two letters form a ligature, however, they
  35. combine into a single glyph. They are then part of the same
  36. cluster and are treated as a unit by the shaping engine &mdash;
  37. even though the two original, underlying letters remain separate
  38. graphemes.
  39. </para>
  40. <para>
  41. HarfBuzz is concerned with clusters, <emphasis>not</emphasis>
  42. with graphemes &mdash; although client programs using HarfBuzz
  43. may still care about graphemes for other reasons from time to time.
  44. </para>
  45. <para>
  46. During the shaping process, there are several shaping operations
  47. that may merge adjacent characters (for example, when two code
  48. points form a ligature or a conjunct form and are replaced by a
  49. single glyph) or split one character into several (for example,
  50. when decomposing a code point through the
  51. <literal>ccmp</literal> feature). Operations like these alter
  52. clusters; HarfBuzz tracks the changes to ensure that no clusters
  53. get lost or broken during shaping.
  54. </para>
  55. <para>
  56. HarfBuzz records cluster information independently from how
  57. shaping operations affect the individual glyphs returned in an
  58. output buffer. Consequently, a client program using HarfBuzz can
  59. utilize the cluster information to implement features such as:
  60. </para>
  61. <itemizedlist>
  62. <listitem>
  63. <para>
  64. Correctly positioning the cursor within a shaped text run,
  65. even when characters have formed ligatures, composed or
  66. decomposed, reordered, or undergone other shaping operations.
  67. </para>
  68. </listitem>
  69. <listitem>
  70. <para>
  71. Correctly highlighting a text selection that includes some,
  72. but not all, of the characters in a word.
  73. </para>
  74. </listitem>
  75. <listitem>
  76. <para>
  77. Applying text attributes (such as color or underlining) to
  78. part, but not all, of a word.
  79. </para>
  80. </listitem>
  81. <listitem>
  82. <para>
  83. Generating output document formats (such as PDF) with
  84. embedded text that can be fully extracted.
  85. </para>
  86. </listitem>
  87. <listitem>
  88. <para>
  89. Determining the mapping between input characters and output
  90. glyphs, such as which glyphs are ligatures.
  91. </para>
  92. </listitem>
  93. <listitem>
  94. <para>
  95. Performing line-breaking, justification, and other
  96. line-level or paragraph-level operations that must be done
  97. after shaping is complete, but which require examining
  98. character-level properties.
  99. </para>
  100. </listitem>
  101. </itemizedlist>
  102. </section>
  103. <section id="working-with-harfbuzz-clusters">
  104. <title>Working with HarfBuzz clusters</title>
  105. <para>
  106. When you add text to a HarfBuzz buffer, each code point must be
  107. assigned a <emphasis>cluster value</emphasis>.
  108. </para>
  109. <para>
  110. This cluster value is an arbitrary number; HarfBuzz uses it only
  111. to distinguish between clusters. Many client programs will use
  112. the index of each code point in the input text stream as the
  113. cluster value. This is for the sake of convenience; the actual
  114. value does not matter.
  115. </para>
  116. <para>
  117. Some of the shaping operations performed by HarfBuzz &mdash;
  118. such as reordering, composition, decomposition, and substitution
  119. &mdash; may alter the cluster values of some characters. The
  120. final cluster values in the buffer at the end of the shaping
  121. process will indicate to client programs which subsequences of
  122. glyphs represent a cluster and, therefore, must not be
  123. separated.
  124. </para>
  125. <para>
  126. In addition, client programs can query the final cluster values
  127. to discern other potentially important information about the
  128. glyphs in the output buffer (such as whether or not a ligature
  129. was formed).
  130. </para>
  131. <para>
  132. For example, if the initial sequence of cluster values was:
  133. </para>
  134. <programlisting>
  135. 0,1,2,3,4
  136. </programlisting>
  137. <para>
  138. and the final sequence of cluster values is:
  139. </para>
  140. <programlisting>
  141. 0,0,3,3
  142. </programlisting>
  143. <para>
  144. then there are two clusters in the output buffer: the first
  145. cluster includes the first two glyphs, and the second cluster
  146. includes the third and fourth glyphs. It is also evident that a
  147. ligature or conjunct has been formed, because there are fewer
  148. glyphs in the output buffer (four) than there were code points
  149. in the input buffer (five).
  150. </para>
  151. <para>
  152. Although client programs using HarfBuzz are free to assign
  153. initial cluster values in any manner they choose to, HarfBuzz
  154. does offer some useful guarantees if the cluster values are
  155. assigned in a monotonic (either non-decreasing or non-increasing)
  156. order.
  157. </para>
  158. <para>
  159. For buffers in the left-to-right (LTR)
  160. or top-to-bottom (TTB) text flow direction,
  161. HarfBuzz will preserve the monotonic property: client programs
  162. are guaranteed that monotonically increasing initial cluster
  163. values will be returned as monotonically increasing final
  164. cluster values.
  165. </para>
  166. <para>
  167. For buffers in the right-to-left (RTL)
  168. or bottom-to-top (BTT) text flow direction,
  169. the directionality of the buffer itself is reversed for final
  170. output as a matter of design. Therefore, HarfBuzz inverts the
  171. monotonic property: client programs are guaranteed that
  172. monotonically increasing initial cluster values will be
  173. returned as monotonically <emphasis>decreasing</emphasis> final
  174. cluster values.
  175. </para>
  176. <para>
  177. Client programs can adjust how HarfBuzz handles clusters during
  178. shaping by setting the
  179. <literal>cluster_level</literal> of the
  180. buffer. HarfBuzz offers three <emphasis>levels</emphasis> of
  181. clustering support for this property:
  182. </para>
  183. <itemizedlist>
  184. <listitem>
  185. <para><emphasis>Level 0</emphasis> is the default.
  186. </para>
  187. <para>
  188. The distinguishing feature of level 0 behavior is that, at
  189. the beginning of processing the buffer, all code points that
  190. are categorized as <emphasis>marks</emphasis>,
  191. <emphasis>modifier symbols</emphasis>, or
  192. <emphasis>Emoji extended pictographic</emphasis> modifiers,
  193. as well as the <emphasis>Zero Width Joiner</emphasis> and
  194. <emphasis>Zero Width Non-Joiner</emphasis> code points, are
  195. assigned the cluster value of the closest preceding code
  196. point from <emphasis>different</emphasis> category.
  197. </para>
  198. <para>
  199. In essence, whenever a base character is followed by a mark
  200. character or a sequence of mark characters, those marks are
  201. reassigned to the same initial cluster value as the base
  202. character. This reassignment is referred to as
  203. "merging" the affected clusters. This behavior is based on
  204. the Grapheme Cluster Boundary specification in <ulink
  205. url="https://www.unicode.org/reports/tr29/#Regex_Definitions">Unicode
  206. Technical Report 29</ulink>.
  207. </para>
  208. <para>
  209. This cluster level is suitable for code that likes to use
  210. HarfBuzz cluster values as an approximation of the Unicode
  211. Grapheme Cluster Boundaries as well.
  212. </para>
  213. <para>
  214. Client programs can specify level 0 behavior for a buffer by
  215. setting its <literal>cluster_level</literal> to
  216. <literal>HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES</literal>.
  217. </para>
  218. </listitem>
  219. <listitem>
  220. <para>
  221. <emphasis>Level 1</emphasis> tweaks the old behavior
  222. slightly to produce better results. Therefore, level 1
  223. clustering is recommended for code that is not required to
  224. implement backward compatibility with the old HarfBuzz.
  225. </para>
  226. <para>
  227. <emphasis>Level 1</emphasis> differs from level 0 by not merging the
  228. clusters of marks and other modifier code points with the
  229. preceding "base" code point's cluster. By preserving the
  230. separate cluster values of these marks and modifier code
  231. points, script shapers can perform additional operations
  232. that might lead to improved results (for example, coloring
  233. mark glyphs differently than their base).
  234. </para>
  235. <para>
  236. Client programs can specify level 1 behavior for a buffer by
  237. setting its <literal>cluster_level</literal> to
  238. <literal>HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS</literal>.
  239. </para>
  240. </listitem>
  241. <listitem>
  242. <para>
  243. <emphasis>Level 2</emphasis> differs significantly in how it
  244. treats cluster values. In level 2, HarfBuzz never merges
  245. clusters.
  246. </para>
  247. <para>
  248. This difference can be seen most clearly when HarfBuzz processes
  249. ligature substitutions and glyph decompositions. In level 0
  250. and level 1, ligatures and glyph decomposition both involve
  251. merging clusters; in level 2, neither of these operations
  252. triggers a merge.
  253. </para>
  254. <para>
  255. Client programs can specify level 2 behavior for a buffer by
  256. setting its <literal>cluster_level</literal> to
  257. <literal>HB_BUFFER_CLUSTER_LEVEL_CHARACTERS</literal>.
  258. </para>
  259. </listitem>
  260. </itemizedlist>
  261. <para>
  262. As mentioned earlier, client programs using HarfBuzz often
  263. assign initial cluster values in a buffer by reusing the indices
  264. of the code points in the input text. This gives a sequence of
  265. cluster values that is monotonically increasing (for example,
  266. 0,1,2,3,4).
  267. </para>
  268. <para>
  269. It is not <emphasis>required</emphasis> that the cluster values
  270. in a buffer be monotonically increasing. However, if the initial
  271. cluster values in a buffer are monotonic and the buffer is
  272. configured to use cluster level 0 or 1, then HarfBuzz
  273. guarantees that the final cluster values in the shaped buffer
  274. will also be monotonic. No such guarantee is made for cluster
  275. level 2.
  276. </para>
  277. <para>
  278. In levels 0 and 1, HarfBuzz implements the following conceptual
  279. model for cluster values:
  280. </para>
  281. <itemizedlist spacing="compact">
  282. <listitem>
  283. <para>
  284. If the sequence of input cluster values is monotonic, the
  285. sequence of cluster values will remain monotonic.
  286. </para>
  287. </listitem>
  288. <listitem>
  289. <para>
  290. Each cluster value represents a single cluster.
  291. </para>
  292. </listitem>
  293. <listitem>
  294. <para>
  295. Each cluster contains one or more glyphs and one or more
  296. characters.
  297. </para>
  298. </listitem>
  299. </itemizedlist>
  300. <para>
  301. In practice, this model offers several benefits. Assuming that
  302. the initial cluster values were monotonically increasing
  303. and distinct before shaping began, then, in the final output:
  304. </para>
  305. <itemizedlist spacing="compact">
  306. <listitem>
  307. <para>
  308. All adjacent glyphs having the same final cluster
  309. value belong to the same cluster.
  310. </para>
  311. </listitem>
  312. <listitem>
  313. <para>
  314. Each character belongs to the cluster that has the highest
  315. cluster value <emphasis>not larger than</emphasis> its
  316. initial cluster value.
  317. </para>
  318. </listitem>
  319. </itemizedlist>
  320. </section>
  321. <section id="a-clustering-example-for-levels-0-and-1">
  322. <title>A clustering example for levels 0 and 1</title>
  323. <para>
  324. The basic shaping operations affect clusters in a predictable
  325. manner when using level 0 or level 1:
  326. </para>
  327. <itemizedlist>
  328. <listitem>
  329. <para>
  330. When two or more clusters <emphasis>merge</emphasis>, the
  331. resulting merged cluster takes as its cluster value the
  332. <emphasis>minimum</emphasis> of the incoming cluster values.
  333. </para>
  334. </listitem>
  335. <listitem>
  336. <para>
  337. When a cluster <emphasis>decomposes</emphasis>, all of the
  338. resulting child clusters inherit as their cluster value the
  339. cluster value of the parent cluster.
  340. </para>
  341. </listitem>
  342. <listitem>
  343. <para>
  344. When a character is <emphasis>reordered</emphasis>, the
  345. reordered character and all clusters that the character
  346. moves past as part of the reordering are merged into one cluster.
  347. </para>
  348. </listitem>
  349. </itemizedlist>
  350. <para>
  351. The functionality, guarantees, and benefits of level 0 and level
  352. 1 behavior can be seen with some examples. First, let us examine
  353. what happens with cluster values when shaping involves cluster
  354. merging with ligatures and decomposition.
  355. </para>
  356. <para>
  357. Let's say we start with the following character sequence (top row) and
  358. initial cluster values (bottom row):
  359. </para>
  360. <programlisting>
  361. A,B,C,D,E
  362. 0,1,2,3,4
  363. </programlisting>
  364. <para>
  365. During shaping, HarfBuzz maps these characters to glyphs from
  366. the font. For simplicity, let us assume that each character maps
  367. to the corresponding, identical-looking glyph:
  368. </para>
  369. <programlisting>
  370. A,B,C,D,E
  371. 0,1,2,3,4
  372. </programlisting>
  373. <para>
  374. Now if, for example, <literal>B</literal> and <literal>C</literal>
  375. form a ligature, then the clusters to which they belong
  376. &quot;merge&quot;. This merged cluster takes for its cluster
  377. value the minimum of all the cluster values of the clusters that
  378. went in to the ligature. In this case, we get:
  379. </para>
  380. <programlisting>
  381. A,BC,D,E
  382. 0,1 ,3,4
  383. </programlisting>
  384. <para>
  385. because 1 is the minimum of the set {1,2}, which were the
  386. cluster values of <literal>B</literal> and
  387. <literal>C</literal>.
  388. </para>
  389. <para>
  390. Next, let us say that the <literal>BC</literal> ligature glyph
  391. decomposes into three components, and <literal>D</literal> also
  392. decomposes into two components. Whenever a cluster decomposes,
  393. its components each inherit the cluster value of their parent:
  394. </para>
  395. <programlisting>
  396. A,BC0,BC1,BC2,D0,D1,E
  397. 0,1 ,1 ,1 ,3 ,3 ,4
  398. </programlisting>
  399. <para>
  400. Next, if <literal>BC2</literal> and <literal>D0</literal> form a
  401. ligature, then their clusters (cluster values 1 and 3) merge into
  402. <literal>min(1,3) = 1</literal>:
  403. </para>
  404. <programlisting>
  405. A,BC0,BC1,BC2D0,D1,E
  406. 0,1 ,1 ,1 ,1 ,4
  407. </programlisting>
  408. <para>
  409. Note that the entirety of cluster 3 merges into cluster 1, not
  410. just the <literal>D0</literal> glyph. This reflects the fact
  411. that the cluster <emphasis>must</emphasis> be treated as an
  412. indivisible unit.
  413. </para>
  414. <para>
  415. At this point, cluster 1 means: the character sequence
  416. <literal>BCD</literal> is represented by glyphs
  417. <literal>BC0,BC1,BC2D0,D1</literal> and cannot be broken down any
  418. further.
  419. </para>
  420. </section>
  421. <section id="reordering-in-levels-0-and-1">
  422. <title>Reordering in levels 0 and 1</title>
  423. <para>
  424. Another common operation in some shapers is glyph
  425. reordering. In order to maintain a monotonic cluster sequence
  426. when glyph reordering takes place, HarfBuzz merges the clusters
  427. of everything in the reordering sequence.
  428. </para>
  429. <para>
  430. For example, let us again start with the character sequence (top
  431. row) and initial cluster values (bottom row):
  432. </para>
  433. <programlisting>
  434. A,B,C,D,E
  435. 0,1,2,3,4
  436. </programlisting>
  437. <para>
  438. If <literal>D</literal> is reordered to the position immediately
  439. before <literal>B</literal>, then HarfBuzz merges the
  440. <literal>B</literal>, <literal>C</literal>, and
  441. <literal>D</literal> clusters &mdash; all the clusters between
  442. the final position of the reordered glyph and its original
  443. position. This means that we get:
  444. </para>
  445. <programlisting>
  446. A,D,B,C,E
  447. 0,1,1,1,4
  448. </programlisting>
  449. <para>
  450. as the final cluster sequence.
  451. </para>
  452. <para>
  453. Merging this many clusters is not ideal, but it is the only
  454. sensible way for HarfBuzz to maintain the guarantee that the
  455. sequence of cluster values remains monotonic and to retain the
  456. true relationship between glyphs and characters.
  457. </para>
  458. </section>
  459. <section id="the-distinction-between-levels-0-and-1">
  460. <title>The distinction between levels 0 and 1</title>
  461. <para>
  462. The preceding examples demonstrate the main effects of using
  463. cluster levels 0 and 1. The only difference between the two
  464. levels is this: in level 0, at the very beginning of the shaping
  465. process, HarfBuzz merges the cluster of each base character
  466. with the clusters of all Unicode marks (combining or not) and
  467. modifiers that follow it.
  468. </para>
  469. <para>
  470. For example, let us start with the following character sequence
  471. (top row) and accompanying initial cluster values (bottom row):
  472. </para>
  473. <programlisting>
  474. A,acute,B
  475. 0,1 ,2
  476. </programlisting>
  477. <para>
  478. The <literal>acute</literal> is a Unicode mark. If HarfBuzz is
  479. using cluster level 0 on this sequence, then the
  480. <literal>A</literal> and <literal>acute</literal> clusters will
  481. merge, and the result will become:
  482. </para>
  483. <programlisting>
  484. A,acute,B
  485. 0,0 ,2
  486. </programlisting>
  487. <para>
  488. This merger is performed before any other script-shaping
  489. steps.
  490. </para>
  491. <para>
  492. This initial cluster merging is the default behavior of the
  493. Windows shaping engine, and the old HarfBuzz codebase copied
  494. that behavior to maintain compatibility. Consequently, it has
  495. remained the default behavior in the new HarfBuzz codebase.
  496. </para>
  497. <para>
  498. But this initial cluster-merging behavior makes it impossible
  499. for client programs to implement some features (such as to
  500. color diacritic marks differently from their base
  501. characters). That is why, in level 1, HarfBuzz does not perform
  502. the initial merging step.
  503. </para>
  504. <para>
  505. For client programs that rely on HarfBuzz cluster values to
  506. perform cursor positioning, level 0 is more convenient. But
  507. relying on cluster boundaries for cursor positioning is wrong: cursor
  508. positions should be determined based on Unicode grapheme
  509. boundaries, not on shaping-cluster boundaries. As such, using
  510. level 1 clustering behavior is recommended.
  511. </para>
  512. <para>
  513. One final facet of levels 0 and 1 is worth noting. HarfBuzz
  514. currently does not allow any
  515. <emphasis>multiple-substitution</emphasis> GSUB lookups to
  516. replace a glyph with zero glyphs (in other words, to delete a
  517. glyph).
  518. </para>
  519. <para>
  520. But, in some other situations, glyphs can be deleted. In
  521. those cases, if the glyph being deleted is the last glyph of its
  522. cluster, HarfBuzz makes sure to merge the deleted glyph's
  523. cluster with a neighboring cluster.
  524. </para>
  525. <para>
  526. This is done primarily to make sure that the starting cluster of the
  527. text always has the cluster index pointing to the start of the text
  528. for the run; more than one client program currently relies on this
  529. guarantee.
  530. </para>
  531. <para>
  532. Incidentally, Apple's CoreText does something different to
  533. maintain the same promise: it inserts a glyph with id 65535 at
  534. the beginning of the glyph string if the glyph corresponding to
  535. the first character in the run was deleted. HarfBuzz might do
  536. something similar in the future.
  537. </para>
  538. </section>
  539. <section id="level-2">
  540. <title>Level 2</title>
  541. <para>
  542. HarfBuzz's level 2 cluster behavior uses a significantly
  543. different model than that of level 0 and level 1.
  544. </para>
  545. <para>
  546. The level 2 behavior is easy to describe, but it may be
  547. difficult to understand in practical terms. In brief, level 2
  548. performs no merging of clusters whatsoever.
  549. </para>
  550. <para>
  551. This means that there is no initial base-and-mark merging step
  552. (as is done in level 0), and it means that reordering moves and
  553. ligature substitutions do not trigger a cluster merge.
  554. </para>
  555. <para>
  556. Only one shaping operation directly affects clusters when using
  557. level 2:
  558. </para>
  559. <itemizedlist>
  560. <listitem>
  561. <para>
  562. When a cluster <emphasis>decomposes</emphasis>, all of the
  563. resulting child clusters inherit as their cluster value the
  564. cluster value of the parent cluster.
  565. </para>
  566. </listitem>
  567. </itemizedlist>
  568. <para>
  569. When glyphs do form a ligature (or when some other feature
  570. substitutes multiple glyphs with one glyph) the cluster value
  571. of the first glyph is retained as the cluster value for the
  572. resulting ligature.
  573. </para>
  574. <para>
  575. This occurrence sounds similar to a cluster merge, but it is
  576. different. In particular, no subsequent characters &mdash;
  577. including marks and modifiers &mdash; are affected. They retain
  578. their previous cluster values.
  579. </para>
  580. <para>
  581. Level 2 cluster behavior is ultimately less complex than level 0
  582. or level 1, but there are several cases for which processing
  583. cluster values produced at level 2 may be tricky.
  584. </para>
  585. <section id="ligatures-with-combining-marks-in-level-2">
  586. <title>Ligatures with combining marks in level 2</title>
  587. <para>
  588. The first example of how HarfBuzz's level 2 cluster behavior
  589. can be tricky is when the text to be shaped includes combining
  590. marks attached to ligatures.
  591. </para>
  592. <para>
  593. Let us start with an input sequence with the following
  594. characters (top row) and initial cluster values (bottom row):
  595. </para>
  596. <programlisting>
  597. A,acute,B,breve,C,circumflex
  598. 0,1 ,2,3 ,4,5
  599. </programlisting>
  600. <para>
  601. If the sequence <literal>A,B,C</literal> forms a ligature,
  602. then these are the cluster values HarfBuzz will return under
  603. the various cluster levels:
  604. </para>
  605. <para>
  606. Level 0:
  607. </para>
  608. <programlisting>
  609. ABC,acute,breve,circumflex
  610. 0 ,0 ,0 ,0
  611. </programlisting>
  612. <para>
  613. Level 1:
  614. </para>
  615. <programlisting>
  616. ABC,acute,breve,circumflex
  617. 0 ,0 ,0 ,5
  618. </programlisting>
  619. <para>
  620. Level 2:
  621. </para>
  622. <programlisting>
  623. ABC,acute,breve,circumflex
  624. 0 ,1 ,3 ,5
  625. </programlisting>
  626. <para>
  627. Making sense of the level 2 result is the hardest for a client
  628. program, because there is nothing in the cluster values that
  629. indicates that <literal>B</literal> and <literal>C</literal>
  630. formed a ligature with <literal>A</literal>.
  631. </para>
  632. <para>
  633. In contrast, the "merged" cluster values of the mark glyphs
  634. that are seen in the level 0 and level 1 output are evidence
  635. that a ligature substitution took place.
  636. </para>
  637. </section>
  638. <section id="reordering-in-level-2">
  639. <title>Reordering in level 2</title>
  640. <para>
  641. Another example of how HarfBuzz's level 2 cluster behavior
  642. can be tricky is when glyphs reorder. Consider an input sequence
  643. with the following characters (top row) and initial cluster
  644. values (bottom row):
  645. </para>
  646. <programlisting>
  647. A,B,C,D,E
  648. 0,1,2,3,4
  649. </programlisting>
  650. <para>
  651. Now imagine <literal>D</literal> moves before
  652. <literal>B</literal> in a reordering operation. The cluster
  653. values will then be:
  654. </para>
  655. <programlisting>
  656. A,D,B,C,E
  657. 0,3,1,2,4
  658. </programlisting>
  659. <para>
  660. Next, if <literal>D</literal> forms a ligature with
  661. <literal>B</literal>, the output is:
  662. </para>
  663. <programlisting>
  664. A,DB,C,E
  665. 0,3 ,2,4
  666. </programlisting>
  667. <para>
  668. However, in a different scenario, in which the shaping rules
  669. of the script instead caused <literal>A</literal> and
  670. <literal>B</literal> to form a ligature
  671. <emphasis>before</emphasis> the <literal>D</literal> reordered, the
  672. result would be:
  673. </para>
  674. <programlisting>
  675. AB,D,C,E
  676. 0 ,3,2,4
  677. </programlisting>
  678. <para>
  679. There is no way for a client program to differentiate between
  680. these two scenarios based on the cluster values
  681. alone. Consequently, client programs that use level 2 might
  682. need to undertake additional work in order to manage cursor
  683. positioning, text attributes, or other desired features.
  684. </para>
  685. </section>
  686. <section id="other-considerations-in-level-2">
  687. <title>Other considerations in level 2</title>
  688. <para>
  689. There may be other problems encountered with ligatures under
  690. level 2, such as if the direction of the text is forced to
  691. the opposite of its natural direction (for example, Arabic text
  692. that is forced into left-to-right directionality). But,
  693. generally speaking, these other scenarios are minor corner
  694. cases that are too obscure for most client programs to need to
  695. worry about.
  696. </para>
  697. </section>
  698. </section>
  699. </chapter>