summaryrefslogtreecommitdiff
path: root/src/hb-ot-shape-complex-indic.cc
diff options
context:
space:
mode:
authorAnas Nashif <anas.nashif@intel.com>2013-01-29 07:06:04 -0800
committerAnas Nashif <anas.nashif@intel.com>2013-01-29 07:06:04 -0800
commit9470543a71d47a3f2c27fc9dbb1e20d03b9cd56e (patch)
tree3e4c9a2e7e721c87c0478909bde9285106b0914d /src/hb-ot-shape-complex-indic.cc
parentbbb5fd4f2e0d0c8cbdd407a39e58de3b40971c52 (diff)
downloadharfbuzz-9470543a71d47a3f2c27fc9dbb1e20d03b9cd56e.tar.gz
harfbuzz-9470543a71d47a3f2c27fc9dbb1e20d03b9cd56e.tar.bz2
harfbuzz-9470543a71d47a3f2c27fc9dbb1e20d03b9cd56e.zip
Imported Upstream version 0.9.12upstream/0.9.12
Diffstat (limited to 'src/hb-ot-shape-complex-indic.cc')
-rw-r--r--src/hb-ot-shape-complex-indic.cc393
1 files changed, 333 insertions, 60 deletions
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 6fbd5c8..5afede0 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -123,6 +123,8 @@ static const indic_config_t indic_configs[] =
{HB_SCRIPT_MALAYALAM, true, 0x0D4D,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_LOG_REPHA},
{HB_SCRIPT_SINHALA, false,0x0DCA,BASE_POS_FIRST,REPH_POS_AFTER_MAIN, REPH_MODE_EXPLICIT},
{HB_SCRIPT_KHMER, false,0x17D2,BASE_POS_FIRST,REPH_POS_DEFAULT, REPH_MODE_VIS_REPHA},
+ /* Myanmar does not have the "old_indic" behavior, even though it has a "new" tag. */
+ {HB_SCRIPT_MYANMAR, false,0x1039,BASE_POS_LAST, REPH_POS_DEFAULT, REPH_MODE_EXPLICIT},
};
@@ -203,6 +205,10 @@ enum {
};
static void
+setup_syllables (const hb_ot_shape_plan_t *plan,
+ hb_font_t *font,
+ hb_buffer_t *buffer);
+static void
initial_reordering (const hb_ot_shape_plan_t *plan,
hb_font_t *font,
hb_buffer_t *buffer);
@@ -216,6 +222,9 @@ collect_features_indic (hb_ot_shape_planner_t *plan)
{
hb_ot_map_builder_t *map = &plan->map;
+ /* Do this before any lookups have been applied. */
+ map->add_gsub_pause (setup_syllables);
+
map->add_bool_feature (HB_TAG('l','o','c','l'));
/* The Indic specs do not require ccmp, but we apply it here since if
* there is a use of it, it's typically at the beginning. */
@@ -240,6 +249,8 @@ override_features_indic (hb_ot_shape_planner_t *plan)
/* Uniscribe does not apply 'kern'. */
if (indic_options ().uniscribe_bug_compatible)
plan->map.add_feature (HB_TAG('k','e','r','n'), 0, true);
+
+ plan->map.add_feature (HB_TAG('l','i','g','a'), 0, true);
}
@@ -254,10 +265,11 @@ struct would_substitute_feature_t
inline bool would_substitute (hb_codepoint_t *glyphs,
unsigned int glyphs_count,
+ bool zero_context,
hb_face_t *face) const
{
for (unsigned int i = 0; i < count; i++)
- if (hb_ot_layout_would_substitute_lookup_fast (face, glyphs, glyphs_count, lookups[i].index))
+ if (hb_ot_layout_lookup_would_substitute_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context))
return true;
return false;
}
@@ -295,6 +307,7 @@ struct indic_shape_plan_t
bool is_old_spec;
hb_codepoint_t virama_glyph;
+ would_substitute_feature_t rphf;
would_substitute_feature_t pref;
would_substitute_feature_t blwf;
would_substitute_feature_t pstf;
@@ -316,9 +329,10 @@ data_create_indic (const hb_ot_shape_plan_t *plan)
break;
}
- indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.get_chosen_script (0) & 0x000000FF) != '2');
+ indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chosen_script[0] & 0x000000FF) != '2');
indic_plan->virama_glyph = (hb_codepoint_t) -1;
+ indic_plan->rphf.init (&plan->map, HB_TAG('r','p','h','f'));
indic_plan->pref.init (&plan->map, HB_TAG('p','r','e','f'));
indic_plan->blwf.init (&plan->map, HB_TAG('b','l','w','f'));
indic_plan->pstf.init (&plan->map, HB_TAG('p','s','t','f'));
@@ -340,13 +354,25 @@ consonant_position_from_face (const indic_shape_plan_t *indic_plan,
hb_codepoint_t *glyphs, unsigned int glyphs_len,
hb_face_t *face)
{
- if (indic_plan->pref.would_substitute (glyphs, glyphs_len, face)) return POS_BELOW_C;
- if (indic_plan->blwf.would_substitute (glyphs, glyphs_len, face)) return POS_BELOW_C;
- if (indic_plan->pstf.would_substitute (glyphs, glyphs_len, face)) return POS_POST_C;
+ bool zero_context = indic_plan->is_old_spec ? false : true;
+ if (indic_plan->pref.would_substitute (glyphs, glyphs_len, zero_context, face)) return POS_BELOW_C;
+ if (indic_plan->blwf.would_substitute (glyphs, glyphs_len, zero_context, face)) return POS_BELOW_C;
+ if (indic_plan->pstf.would_substitute (glyphs, glyphs_len, zero_context, face)) return POS_POST_C;
return POS_BASE_C;
}
+enum syllable_type_t {
+ consonant_syllable,
+ vowel_syllable,
+ standalone_cluster,
+ broken_cluster,
+ non_indic_cluster,
+};
+
+#include "hb-ot-shape-complex-indic-machine.hh"
+
+
static void
setup_masks_indic (const hb_ot_shape_plan_t *plan HB_UNUSED,
hb_buffer_t *buffer,
@@ -363,6 +389,14 @@ setup_masks_indic (const hb_ot_shape_plan_t *plan HB_UNUSED,
set_indic_properties (buffer->info[i]);
}
+static void
+setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
+ hb_font_t *font HB_UNUSED,
+ hb_buffer_t *buffer)
+{
+ find_syllables (buffer);
+}
+
static int
compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
{
@@ -400,7 +434,9 @@ update_consonant_positions (const hb_ot_shape_plan_t *plan,
* https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */
static void
-initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, hb_buffer_t *buffer,
+initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
+ hb_face_t *face,
+ hb_buffer_t *buffer,
unsigned int start, unsigned int end)
{
const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
@@ -431,22 +467,36 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, hb_buffer
unsigned int limit = start;
if (indic_plan->mask_array[RPHF] &&
start + 3 <= end &&
- info[start].indic_category() == OT_Ra &&
- info[start + 1].indic_category() == OT_H &&
- (/* TODO Handle other Reph modes. */
+ (
(indic_plan->config->reph_mode == REPH_MODE_IMPLICIT && !is_joiner (info[start + 2])) ||
(indic_plan->config->reph_mode == REPH_MODE_EXPLICIT && info[start + 2].indic_category() == OT_ZWJ)
))
{
- limit += 2;
- while (limit < end && is_joiner (info[limit]))
- limit++;
- base = start;
- has_reph = true;
- };
-
- switch (indic_plan->config->base_pos == BASE_POS_LAST)
+ /* See if it matches the 'rphf' feature. */
+ hb_codepoint_t glyphs[2] = {info[start].codepoint, info[start + 1].codepoint};
+ if (indic_plan->rphf.would_substitute (glyphs, ARRAY_LENGTH (glyphs), true, face))
+ {
+ limit += 2;
+ while (limit < end && is_joiner (info[limit]))
+ limit++;
+ base = start;
+ has_reph = true;
+ }
+ } else if (indic_plan->config->reph_mode == REPH_MODE_LOG_REPHA && info[start].indic_category() == OT_Repha)
+ {
+ limit += 1;
+ while (limit < end && is_joiner (info[limit]))
+ limit++;
+ base = start;
+ has_reph = true;
+ }
+
+ switch (indic_plan->config->base_pos)
{
+ default:
+ assert (false);
+ /* fallthrough */
+
case BASE_POS_LAST:
{
/* -> starting from the end of the syllable, move backwards */
@@ -486,7 +536,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, hb_buffer
* half form.
* A ZWJ before a Halant, requests a subjoined form instead, and hence
* search continues. This is particularly important for Bengali
- * sequence Ra,H,Ya that shouls form Ya-Phalaa by subjoining Ya. */
+ * sequence Ra,H,Ya that should form Ya-Phalaa by subjoining Ya. */
if (start < i &&
info[i].indic_category() == OT_ZWJ &&
info[i - 1].indic_category() == OT_H)
@@ -520,9 +570,6 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, hb_buffer
info[i].indic_position() = POS_BELOW_C;
}
break;
-
- default:
- abort ();
}
/* -> If the syllable starts with Ra + Halant (in a script that has Reph)
@@ -530,7 +577,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, hb_buffer
* base consonants.
*
* Only do this for unforced Reph. (ie. not for Ra,H,ZWJ. */
- if (has_reph && base == start && start + 2 == limit) {
+ if (has_reph && base == start && start - limit <= 2) {
/* Have no other consonant, so Reph is not formed and Ra becomes base. */
has_reph = false;
}
@@ -597,15 +644,16 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, hb_buffer
info[start].indic_position() = POS_RA_TO_BECOME_REPH;
/* For old-style Indic script tags, move the first post-base Halant after
- * last consonant. */
+ * last consonant. Only do this if there is *not* a Halant after last
+ * consonant. Otherwise it becomes messy. */
if (indic_plan->is_old_spec) {
for (unsigned int i = base + 1; i < end; i++)
if (info[i].indic_category() == OT_H) {
unsigned int j;
for (j = end - 1; j > i; j--)
- if (is_consonant (info[j]))
+ if (is_consonant (info[j]) || info[j].indic_category() == OT_H)
break;
- if (j > i) {
+ if (info[j].indic_category() != OT_H && j > i) {
/* Move Halant to after last consonant. */
hb_glyph_info_t t = info[i];
memmove (&info[i], &info[i + 1], (j - i) * sizeof (info[0]));
@@ -623,13 +671,17 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, hb_buffer
if ((FLAG (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | HALANT_OR_COENG_FLAGS)))
{
info[i].indic_position() = last_pos;
- if (unlikely (indic_options ().uniscribe_bug_compatible &&
- info[i].indic_category() == OT_H &&
+ if (unlikely (info[i].indic_category() == OT_H &&
info[i].indic_position() == POS_PRE_M))
{
/*
* Uniscribe doesn't move the Halant with Left Matra.
* TEST: U+092B,U+093F,U+094DE
+ * We follow. This is important for the Sinhala
+ * U+0DDA split matra since it decomposes to U+0DD9,U+0DCA
+ * where U+0DD9 is a left matra and U+0DCA is the virama.
+ * We don't want to move the virama with the left matra.
+ * TEST: U+0D9A,U+0DDA
*/
for (unsigned int j = i; j > start; j--)
if (info[j - 1].indic_position() != POS_PRE_M) {
@@ -694,13 +746,12 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, hb_buffer
info[i].mask |= mask;
}
- /* XXX This will not match for old-Indic spec since the Halant-Ra order is reversed already. */
if (indic_plan->mask_array[PREF] && base + 2 < end)
{
/* Find a Halant,Ra sequence and mark it for pre-base reordering processing. */
- for (unsigned int i = base + 1; i + 1 < end; i++)
- if (is_halant_or_coeng (info[i]) &&
- info[i + 1].indic_category() == OT_Ra)
+ for (unsigned int i = base + 1; i + 1 < end; i++) {
+ hb_codepoint_t glyphs[2] = {info[i].codepoint, info[i + 1].codepoint};
+ if (indic_plan->pref.would_substitute (glyphs, ARRAY_LENGTH (glyphs), true, face))
{
info[i++].mask |= indic_plan->mask_array[PREF];
info[i++].mask |= indic_plan->mask_array[PREF];
@@ -716,6 +767,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, hb_buffer
break;
}
+ }
}
/* Apply ZWJ/ZWNJ effects */
@@ -741,15 +793,17 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, hb_buffer
static void
initial_reordering_vowel_syllable (const hb_ot_shape_plan_t *plan,
+ hb_face_t *face,
hb_buffer_t *buffer,
unsigned int start, unsigned int end)
{
/* We made the vowels look like consonants. So let's call the consonant logic! */
- initial_reordering_consonant_syllable (plan, buffer, start, end);
+ initial_reordering_consonant_syllable (plan, face, buffer, start, end);
}
static void
initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan,
+ hb_face_t *face,
hb_buffer_t *buffer,
unsigned int start, unsigned int end)
{
@@ -765,19 +819,103 @@ initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan,
return;
}
- initial_reordering_consonant_syllable (plan, buffer, start, end);
+ initial_reordering_consonant_syllable (plan, face, buffer, start, end);
}
static void
-initial_reordering_non_indic (const hb_ot_shape_plan_t *plan HB_UNUSED,
- hb_buffer_t *buffer HB_UNUSED,
- unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
+initial_reordering_broken_cluster (const hb_ot_shape_plan_t *plan,
+ hb_face_t *face,
+ hb_buffer_t *buffer,
+ unsigned int start, unsigned int end)
+{
+ /* We already inserted dotted-circles, so just call the standalone_cluster. */
+ initial_reordering_standalone_cluster (plan, face, buffer, start, end);
+}
+
+static void
+initial_reordering_non_indic_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED,
+ hb_face_t *face HB_UNUSED,
+ hb_buffer_t *buffer HB_UNUSED,
+ unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
{
/* Nothing to do right now. If we ever switch to using the output
* buffer in the reordering process, we'd need to next_glyph() here. */
}
-#include "hb-ot-shape-complex-indic-machine.hh"
+
+static void
+initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
+ hb_face_t *face,
+ hb_buffer_t *buffer,
+ unsigned int start, unsigned int end)
+{
+ syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
+ switch (syllable_type) {
+ case consonant_syllable: initial_reordering_consonant_syllable (plan, face, buffer, start, end); return;
+ case vowel_syllable: initial_reordering_vowel_syllable (plan, face, buffer, start, end); return;
+ case standalone_cluster: initial_reordering_standalone_cluster (plan, face, buffer, start, end); return;
+ case broken_cluster: initial_reordering_broken_cluster (plan, face, buffer, start, end); return;
+ case non_indic_cluster: initial_reordering_non_indic_cluster (plan, face, buffer, start, end); return;
+ }
+}
+
+static inline void
+insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
+ hb_font_t *font,
+ hb_buffer_t *buffer)
+{
+ /* Note: This loop is extra overhead, but should not be measurable. */
+ bool has_broken_syllables = false;
+ unsigned int count = buffer->len;
+ for (unsigned int i = 0; i < count; i++)
+ if ((buffer->info[i].syllable() & 0x0F) == broken_cluster) {
+ has_broken_syllables = true;
+ break;
+ }
+ if (likely (!has_broken_syllables))
+ return;
+
+
+ hb_codepoint_t dottedcircle_glyph;
+ if (!font->get_glyph (0x25CC, 0, &dottedcircle_glyph))
+ return;
+
+ hb_glyph_info_t dottedcircle = {0};
+ dottedcircle.codepoint = 0x25CC;
+ set_indic_properties (dottedcircle);
+ dottedcircle.codepoint = dottedcircle_glyph;
+
+ buffer->clear_output ();
+
+ buffer->idx = 0;
+ unsigned int last_syllable = 0;
+ while (buffer->idx < buffer->len)
+ {
+ unsigned int syllable = buffer->cur().syllable();
+ syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F);
+ if (unlikely (last_syllable != syllable && syllable_type == broken_cluster))
+ {
+ last_syllable = syllable;
+
+ hb_glyph_info_t info = dottedcircle;
+ info.cluster = buffer->cur().cluster;
+ info.mask = buffer->cur().mask;
+ info.syllable() = buffer->cur().syllable();
+
+ /* Insert dottedcircle after possible Repha. */
+ while (buffer->idx < buffer->len &&
+ last_syllable == buffer->cur().syllable() &&
+ buffer->cur().indic_category() == OT_Repha)
+ buffer->next_glyph ();
+
+ buffer->output_info (info);
+ }
+ else
+ buffer->next_glyph ();
+ }
+
+ buffer->swap_buffers ();
+}
static void
initial_reordering (const hb_ot_shape_plan_t *plan,
@@ -785,7 +923,20 @@ initial_reordering (const hb_ot_shape_plan_t *plan,
hb_buffer_t *buffer)
{
update_consonant_positions (plan, font, buffer);
- find_syllables (plan, buffer);
+ insert_dotted_circles (plan, font, buffer);
+
+ hb_glyph_info_t *info = buffer->info;
+ unsigned int count = buffer->len;
+ if (unlikely (!count)) return;
+ unsigned int last = 0;
+ unsigned int last_syllable = info[0].syllable();
+ for (unsigned int i = 1; i < count; i++)
+ if (last_syllable != info[i].syllable()) {
+ initial_reordering_syllable (plan, font->face, buffer, last, i);
+ last = i;
+ last_syllable = info[last].syllable();
+ }
+ initial_reordering_syllable (plan, font->face, buffer, last, count);
}
static void
@@ -829,11 +980,11 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan,
/* If we lost track of base, alas, position before last thingy. */
unsigned int new_pos = base == end ? base - 2 : base - 1;
- /* Malayalam does not have "half" forms or explicit virama forms.
- * The glyphs formed by 'half' are Chillus. We want to position
- * matra after them all.
+ /* Malayalam / Tamil do not have "half" forms or explicit virama forms.
+ * The glyphs formed by 'half' are Chillus or ligated explicit viramas.
+ * We want to position matra after them.
*/
- if (buffer->props.script != HB_SCRIPT_MALAYALAM)
+ if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL)
{
while (new_pos > start &&
!(is_one_of (info[new_pos], (FLAG (OT_M) | FLAG (OT_H) | FLAG (OT_Coeng)))))
@@ -853,7 +1004,7 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan,
new_pos = start; /* No move. */
}
- if (start < new_pos)
+ if (start < new_pos && info[new_pos].indic_position () != POS_PRE_M)
{
/* Now go see if there's actually any matras... */
for (unsigned int i = new_pos; i > start; i--)
@@ -1045,21 +1196,28 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan,
*/
unsigned int new_pos = base;
- while (new_pos > start &&
- !(is_one_of (info[new_pos - 1], FLAG(OT_M) | HALANT_OR_COENG_FLAGS)))
- new_pos--;
-
- /* In Khmer coeng model, a V,Ra can go *after* matras. If it goes after a
- * split matra, it should be reordered to *before* the left part of such matra. */
- if (new_pos > start && info[new_pos - 1].indic_category() == OT_M)
+ /* Malayalam / Tamil do not have "half" forms or explicit virama forms.
+ * The glyphs formed by 'half' are Chillus or ligated explicit viramas.
+ * We want to position matra after them.
+ */
+ if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL)
{
- unsigned int old_pos = i;
- for (unsigned int i = base + 1; i < old_pos; i++)
- if (info[i].indic_category() == OT_M)
- {
- new_pos--;
- break;
- }
+ while (new_pos > start &&
+ !(is_one_of (info[new_pos - 1], FLAG(OT_M) | HALANT_OR_COENG_FLAGS)))
+ new_pos--;
+
+ /* In Khmer coeng model, a V,Ra can go *after* matras. If it goes after a
+ * split matra, it should be reordered to *before* the left part of such matra. */
+ if (new_pos > start && info[new_pos - 1].indic_category() == OT_M)
+ {
+ unsigned int old_pos = i;
+ for (unsigned int i = base + 1; i < old_pos; i++)
+ if (info[i].indic_category() == OT_M)
+ {
+ new_pos--;
+ break;
+ }
+ }
}
if (new_pos > start && is_halant_or_coeng (info[new_pos - 1]))
@@ -1105,11 +1263,11 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan,
static void
final_reordering (const hb_ot_shape_plan_t *plan,
- hb_font_t *font,
+ hb_font_t *font HB_UNUSED,
hb_buffer_t *buffer)
{
unsigned int count = buffer->len;
- if (!count) return;
+ if (unlikely (!count)) return;
hb_glyph_info_t *info = buffer->info;
unsigned int last = 0;
@@ -1122,11 +1280,123 @@ final_reordering (const hb_ot_shape_plan_t *plan,
}
final_reordering_syllable (plan, buffer, last, count);
+ /* Zero syllables now... */
+ for (unsigned int i = 0; i < count; i++)
+ info[i].syllable() = 0;
+
HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category);
HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position);
}
+static hb_ot_shape_normalization_mode_t
+normalization_preference_indic (const hb_segment_properties_t *props HB_UNUSED)
+{
+ return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT;
+}
+
+static bool
+decompose_indic (const hb_ot_shape_normalize_context_t *c,
+ hb_codepoint_t ab,
+ hb_codepoint_t *a,
+ hb_codepoint_t *b)
+{
+ switch (ab)
+ {
+ /* Don't decompose these. */
+ case 0x0931 : return false;
+ case 0x0B94 : return false;
+
+
+ /*
+ * Decompose split matras that don't have Unicode decompositions.
+ */
+
+ case 0x0F77 : *a = 0x0FB2; *b= 0x0F81; return true;
+ case 0x0F79 : *a = 0x0FB3; *b= 0x0F81; return true;
+ case 0x17BE : *a = 0x17C1; *b= 0x17BE; return true;
+ case 0x17BF : *a = 0x17C1; *b= 0x17BF; return true;
+ case 0x17C0 : *a = 0x17C1; *b= 0x17C0; return true;
+ case 0x17C4 : *a = 0x17C1; *b= 0x17C4; return true;
+ case 0x17C5 : *a = 0x17C1; *b= 0x17C5; return true;
+ case 0x1925 : *a = 0x1920; *b= 0x1923; return true;
+ case 0x1926 : *a = 0x1920; *b= 0x1924; return true;
+ case 0x1B3C : *a = 0x1B42; *b= 0x1B3C; return true;
+ case 0x1112E : *a = 0x11127; *b= 0x11131; return true;
+ case 0x1112F : *a = 0x11127; *b= 0x11132; return true;
+#if 0
+ /* This one has no decomposition in Unicode, but needs no decomposition either. */
+ /* case 0x0AC9 : return false; */
+ case 0x0B57 : *a = no decomp, -> RIGHT; return true;
+ case 0x1C29 : *a = no decomp, -> LEFT; return true;
+ case 0xA9C0 : *a = no decomp, -> RIGHT; return true;
+ case 0x111BF : *a = no decomp, -> ABOVE; return true;
+#endif
+ }
+
+ if ((ab == 0x0DDA || hb_in_range<hb_codepoint_t> (ab, 0x0DDC, 0x0DDE)))
+ {
+ /*
+ * Sinhala split matras... Let the fun begin.
+ *
+ * These four characters have Unicode decompositions. However, Uniscribe
+ * decomposes them "Khmer-style", that is, it uses the character itself to
+ * get the second half. The first half of all four decompositions is always
+ * U+0DD9.
+ *
+ * Now, there are buggy fonts, namely, the widely used lklug.ttf, that are
+ * broken with Uniscribe. But we need to support them. As such, we only
+ * do the Uniscribe-style decomposition if the character is transformed into
+ * its "sec.half" form by the 'pstf' feature. Otherwise, we fall back to
+ * Unicode decomposition.
+ *
+ * Note that we can't unconditionally use Unicode decomposition. That would
+ * break some other fonts, that are designed to work with Uniscribe, and
+ * don't have positioning features for the Unicode-style decomposition.
+ *
+ * Argh...
+ *
+ * The Uniscribe behavior is now documented in the newly published Sinhala
+ * spec in 2012:
+ *
+ * http://www.microsoft.com/typography/OpenTypeDev/sinhala/intro.htm#shaping
+ */
+
+ const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) c->plan->data;
+
+ hb_codepoint_t glyph;
+
+ if (indic_options ().uniscribe_bug_compatible ||
+ (c->font->get_glyph (ab, 0, &glyph) &&
+ indic_plan->pstf.would_substitute (&glyph, 1, true, c->font->face)))
+ {
+ /* Ok, safe to use Uniscribe-style decomposition. */
+ *a = 0x0DD9;
+ *b = ab;
+ return true;
+ }
+ }
+
+ return c->unicode->decompose (ab, a, b);
+}
+
+static bool
+compose_indic (const hb_ot_shape_normalize_context_t *c,
+ hb_codepoint_t a,
+ hb_codepoint_t b,
+ hb_codepoint_t *ab)
+{
+ /* Avoid recomposing split matras. */
+ if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a)))
+ return false;
+
+ /* Composition-exclusion exceptions that we want to recompose. */
+ if (a == 0x09AF && b == 0x09BC) { *ab = 0x09DF; return true; }
+
+ return c->unicode->compose (a, b, ab);
+}
+
+
const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic =
{
"indic",
@@ -1135,7 +1405,10 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic =
data_create_indic,
data_destroy_indic,
NULL, /* preprocess_text */
- NULL, /* normalization_preference */
+ normalization_preference_indic,
+ decompose_indic,
+ compose_indic,
setup_masks_indic,
false, /* zero_width_attached_marks */
+ false, /* fallback_position */
};