Index: third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic.cc |
diff --git a/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic.cc b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic.cc |
index cde02e0a567f2b9e6ad2839ffb6159668accce48..507547714099371c602fab70b9b9c2f41f53955e 100644 |
--- a/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic.cc |
+++ b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic.cc |
@@ -28,9 +28,38 @@ |
#include "hb-ot-shape-private.hh" |
+#ifndef HB_DEBUG_ARABIC |
+#define HB_DEBUG_ARABIC (HB_DEBUG+0) |
+#endif |
+ |
+ |
/* buffer var allocations */ |
#define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */ |
+#define HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH HB_BUFFER_SCRATCH_FLAG_COMPLEX0 |
+ |
+/* See: |
+ * https://github.com/behdad/harfbuzz/commit/6e6f82b6f3dde0fc6c3c7d991d9ec6cfff57823d#commitcomment-14248516 */ |
+#define HB_ARABIC_GENERAL_CATEGORY_IS_WORD(gen_cat) \ |
+ (FLAG_SAFE (gen_cat) & \ |
+ (FLAG (HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED) | \ |
+ FLAG (HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE) | \ |
+ /*FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) |*/ \ |
+ FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) | \ |
+ FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) | \ |
+ /*FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) |*/ \ |
+ /*FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER) |*/ \ |
+ FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \ |
+ FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \ |
+ FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) | \ |
+ FLAG (HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) | \ |
+ FLAG (HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER) | \ |
+ FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER) | \ |
+ FLAG (HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL) | \ |
+ FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL) | \ |
+ FLAG (HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL) | \ |
+ FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL))) |
+ |
/* |
* Joining types: |
@@ -84,7 +113,7 @@ static const hb_tag_t arabic_features[] = |
/* Same order as the feature array */ |
-enum { |
+enum arabic_action_t { |
ISOL, |
FINA, |
FIN2, |
@@ -95,7 +124,11 @@ enum { |
NONE, |
- ARABIC_NUM_FEATURES = NONE |
+ ARABIC_NUM_FEATURES = NONE, |
+ |
+ /* We abuse the same byte for other things... */ |
+ STCH_FIXED, |
+ STCH_REPEATING, |
}; |
static const struct arabic_state_table_entry { |
@@ -140,6 +173,11 @@ arabic_fallback_shape (const hb_ot_shape_plan_t *plan, |
hb_buffer_t *buffer); |
static void |
+record_stch (const hb_ot_shape_plan_t *plan, |
+ hb_font_t *font, |
+ hb_buffer_t *buffer); |
+ |
+static void |
collect_features_arabic (hb_ot_shape_planner_t *plan) |
{ |
hb_ot_map_builder_t *map = &plan->map; |
@@ -165,6 +203,9 @@ collect_features_arabic (hb_ot_shape_planner_t *plan) |
map->add_gsub_pause (nuke_joiners); |
+ map->add_global_bool_feature (HB_TAG('s','t','c','h')); |
+ map->add_gsub_pause (record_stch); |
+ |
map->add_global_bool_feature (HB_TAG('c','c','m','p')); |
map->add_global_bool_feature (HB_TAG('l','o','c','l')); |
@@ -208,8 +249,10 @@ struct arabic_shape_plan_t |
* mask_array[NONE] == 0. */ |
hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1]; |
- bool do_fallback; |
arabic_fallback_plan_t *fallback_plan; |
+ |
+ unsigned int do_fallback : 1; |
+ unsigned int has_stch : 1; |
}; |
void * |
@@ -220,6 +263,7 @@ data_create_arabic (const hb_ot_shape_plan_t *plan) |
return NULL; |
arabic_plan->do_fallback = plan->props.script == HB_SCRIPT_ARABIC; |
+ arabic_plan->has_stch = !!plan->map.get_1_mask (HB_TAG ('s','t','c','h')); |
for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) { |
arabic_plan->mask_array[i] = plan->map.get_1_mask (arabic_features[i]); |
arabic_plan->do_fallback = arabic_plan->do_fallback && |
@@ -320,8 +364,6 @@ setup_masks_arabic_plan (const arabic_shape_plan_t *arabic_plan, |
hb_glyph_info_t *info = buffer->info; |
for (unsigned int i = 0; i < count; i++) |
info[i].mask |= arabic_plan->mask_array[info[i].arabic_shaping_action()]; |
- |
- HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); |
} |
static void |
@@ -371,6 +413,194 @@ retry: |
arabic_fallback_plan_shape (fallback_plan, font, buffer); |
} |
+/* |
+ * Stretch feature: "stch". |
+ * See example here: |
+ * https://www.microsoft.com/typography/OpenTypeDev/syriac/intro.htm |
+ * We implement this in a generic way, such that the Arabic subtending |
+ * marks can use it as well. |
+ */ |
+ |
+static void |
+record_stch (const hb_ot_shape_plan_t *plan, |
+ hb_font_t *font, |
+ hb_buffer_t *buffer) |
+{ |
+ const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; |
+ if (!arabic_plan->has_stch) |
+ return; |
+ |
+ /* 'stch' feature was just applied. Look for anything that multiplied, |
+ * and record it for stch treatment later. Note that rtlm, frac, etc |
+ * are applied before stch, but we assume that they didn't result in |
+ * anything multiplying into 5 pieces, so it's safe-ish... */ |
+ |
+ unsigned int count = buffer->len; |
+ hb_glyph_info_t *info = buffer->info; |
+ for (unsigned int i = 0; i < count; i++) |
+ if (unlikely (_hb_glyph_info_multiplied (&info[i]))) |
+ { |
+ unsigned int comp = _hb_glyph_info_get_lig_comp (&info[i]); |
+ info[i].arabic_shaping_action() = comp % 2 ? STCH_REPEATING : STCH_FIXED; |
+ buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH; |
+ } |
+} |
+ |
+static void |
+apply_stch (const hb_ot_shape_plan_t *plan, |
+ hb_buffer_t *buffer, |
+ hb_font_t *font) |
+{ |
+ if (likely (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH))) |
+ return; |
+ |
+ /* The Arabic shaper currently always processes in RTL mode, so we should |
+ * stretch / position the stretched pieces to the left / preceding glyphs. */ |
+ |
+ /* We do a two pass implementation: |
+ * First pass calculates the exact number of extra glyphs we need, |
+ * We then enlarge buffer to have that much room, |
+ * Second pass applies the stretch, copying things to the end of buffer. |
+ */ |
+ |
+ /* 30 = 2048 / 70. |
+ * https://www.microsoft.com/typography/cursivescriptguidelines.mspx */ |
+ hb_position_t overlap = font->x_scale / 30; |
+ DEBUG_MSG (ARABIC, NULL, "overlap for stretching is %d", overlap); |
+ int sign = font->x_scale < 0 ? -1 : +1; |
+ unsigned int extra_glyphs_needed = 0; // Set during MEASURE, used during CUT |
+ typedef enum { MEASURE, CUT } step_t; |
+ |
+ for (step_t step = MEASURE; step <= CUT; step = (step_t) (step + 1)) |
+ { |
+ unsigned int count = buffer->len; |
+ hb_glyph_info_t *info = buffer->info; |
+ hb_glyph_position_t *pos = buffer->pos; |
+ unsigned int new_len = count + extra_glyphs_needed; // write head during CUT |
+ unsigned int j = new_len; |
+ for (unsigned int i = count; i; i--) |
+ { |
+ if (!hb_in_range<unsigned> (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING)) |
+ { |
+ if (step == CUT) |
+ { |
+ --j; |
+ info[j] = info[i - 1]; |
+ pos[j] = pos[i - 1]; |
+ } |
+ continue; |
+ } |
+ |
+ /* Yay, justification! */ |
+ |
+ hb_position_t w_total = 0; // Total to be filled |
+ hb_position_t w_fixed = 0; // Sum of fixed tiles |
+ hb_position_t w_repeating = 0; // Sum of repeating tiles |
+ int n_fixed = 0; |
+ int n_repeating = 0; |
+ |
+ unsigned int end = i; |
+ while (i && |
+ hb_in_range<unsigned> (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING)) |
+ { |
+ i--; |
+ hb_glyph_extents_t extents; |
+ if (!font->get_glyph_extents (info[i].codepoint, &extents)) |
+ extents.width = 0; |
+ extents.width -= overlap; |
+ if (info[i].arabic_shaping_action() == STCH_FIXED) |
+ { |
+ w_fixed += extents.width; |
+ n_fixed++; |
+ } |
+ else |
+ { |
+ w_repeating += extents.width; |
+ n_repeating++; |
+ } |
+ } |
+ unsigned int start = i; |
+ unsigned int context = i; |
+ while (context && |
+ !hb_in_range<unsigned> (info[context - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING) && |
+ (_hb_glyph_info_is_default_ignorable (&info[context - 1]) || |
+ HB_ARABIC_GENERAL_CATEGORY_IS_WORD (_hb_glyph_info_get_general_category (&info[context - 1])))) |
+ { |
+ context--; |
+ w_total += pos[context].x_advance; |
+ } |
+ i++; // Don't touch i again. |
+ |
+ DEBUG_MSG (ARABIC, NULL, "%s stretch at (%d,%d,%d)", |
+ step == MEASURE ? "measuring" : "cutting", context, start, end); |
+ DEBUG_MSG (ARABIC, NULL, "rest of word: count=%d width %d", start - context, w_total); |
+ DEBUG_MSG (ARABIC, NULL, "fixed tiles: count=%d width=%d", n_fixed, w_fixed); |
+ DEBUG_MSG (ARABIC, NULL, "repeating tiles: count=%d width=%d", n_repeating, w_repeating); |
+ |
+ /* Number of additional times to repeat each repeating tile. */ |
+ int n_copies = 0; |
+ |
+ hb_position_t w_remaining = w_total - w_fixed - overlap; |
+ if (sign * w_remaining > sign * w_repeating && sign * w_repeating > 0) |
+ n_copies = (sign * w_remaining + sign * w_repeating / 4) / (sign * w_repeating) - 1; |
+ |
+ if (step == MEASURE) |
+ { |
+ extra_glyphs_needed += n_copies * n_repeating; |
+ DEBUG_MSG (ARABIC, NULL, "will add extra %d copies of repeating tiles", n_copies); |
+ } |
+ else |
+ { |
+ hb_position_t x_offset = -overlap; |
+ for (unsigned int k = end; k > start; k--) |
+ { |
+ hb_glyph_extents_t extents; |
+ if (!font->get_glyph_extents (info[k - 1].codepoint, &extents)) |
+ extents.width = 0; |
+ extents.width -= overlap; |
+ |
+ unsigned int repeat = 1; |
+ if (info[k - 1].arabic_shaping_action() == STCH_REPEATING) |
+ repeat += n_copies; |
+ |
+ DEBUG_MSG (ARABIC, NULL, "appending %d copies of glyph %d; j=%d", |
+ repeat, info[k - 1].codepoint, j); |
+ for (unsigned int n = 0; n < repeat; n++) |
+ { |
+ x_offset -= extents.width; |
+ pos[k - 1].x_offset = x_offset; |
+ /* Append copy. */ |
+ --j; |
+ info[j] = info[k - 1]; |
+ pos[j] = pos[k - 1]; |
+ } |
+ } |
+ } |
+ } |
+ |
+ if (step == MEASURE) |
+ { |
+ if (unlikely (!buffer->ensure (count + extra_glyphs_needed))) |
+ break; |
+ } |
+ else |
+ { |
+ assert (j == 0); |
+ buffer->len = new_len; |
+ } |
+ } |
+} |
+ |
+ |
+static void |
+postprocess_glyphs_arabic (const hb_ot_shape_plan_t *plan, |
+ hb_buffer_t *buffer, |
+ hb_font_t *font) |
+{ |
+ apply_stch (plan, buffer, font); |
+ |
+ HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); |
+} |
const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic = |
{ |
@@ -379,7 +609,8 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic = |
NULL, /* override_features */ |
data_create_arabic, |
data_destroy_arabic, |
- NULL, /* preprocess_text_arabic */ |
+ NULL, /* preprocess_text */ |
+ postprocess_glyphs_arabic, |
HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, |
NULL, /* decompose */ |
NULL, /* compose */ |