From fabd3113a98c5f4114f48920fa7ea38bd65a8d32 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 5 Sep 2012 22:19:28 -0400 Subject: [PATCH] [OT] Port Arabic fallback shaping to synthetic GSUB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All of init/medi/fina/isol and rlig implemented. Let there be dragons... ⻯ --- src/Makefile.am | 1 + src/hb-open-type-private.hh | 69 ++++-- src/hb-ot-layout-common-private.hh | 9 +- src/hb-ot-layout-gsub-table.hh | 11 +- src/hb-ot-map-private.hh | 6 + src/hb-ot-map.cc | 1 + src/hb-ot-shape-complex-arabic-fallback.hh | 252 +++++++++++++++++++++ src/hb-ot-shape-complex-arabic.cc | 170 +++++--------- src/hb-ot-shape.cc | 7 +- src/hb-private.hh | 11 +- 10 files changed, 375 insertions(+), 162 deletions(-) create mode 100644 src/hb-ot-shape-complex-arabic-fallback.hh diff --git a/src/Makefile.am b/src/Makefile.am index d7c4560b6..609635c9f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -78,6 +78,7 @@ HBSOURCES += \ hb-ot-map-private.hh \ hb-ot-shape.cc \ hb-ot-shape-complex-arabic.cc \ + hb-ot-shape-complex-arabic-fallback.hh \ hb-ot-shape-complex-arabic-table.hh \ hb-ot-shape-complex-indic.cc \ hb-ot-shape-complex-indic-machine.hh \ diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh index c273a9795..7106a2e82 100644 --- a/src/hb-open-type-private.hh +++ b/src/hb-open-type-private.hh @@ -342,38 +342,52 @@ struct Sanitizer struct hb_serialize_context_t { - inline void init (void *start, unsigned int size) + inline hb_serialize_context_t (void *start, unsigned int size) { this->start = (char *) start; this->end = this->start + size; - } - inline void start_processing (void) - { this->ran_out_of_room = false; this->head = this->start; this->debug_depth = 0; + } + template + inline Type *start_serialize (void) + { DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1, "start [%p..%p] (%lu bytes)", this->start, this->end, (unsigned long) (this->end - this->start)); + + return start_embed (); } - inline void end_processing (void) + inline void end_serialize (void) { DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1, - "end [%p..%p] %s", + "end [%p..%p] serialized %d bytes; %s", this->start, this->end, + (int) (this->head - this->start), this->ran_out_of_room ? "RAN OUT OF ROOM" : "did not ran out of room"); - this->start = this->end = this->head = NULL; + } + + template + inline Type *copy (void) + { + assert (!this->ran_out_of_room); + unsigned int len = this->head - this->start; + void *p = malloc (len); + if (p) + memcpy (p, this->start, len); + return reinterpret_cast (p); } template inline Type *allocate_size (unsigned int size) { - if (unlikely (this->ran_out_of_room || this->end - this->head > size)) { + if (unlikely (this->ran_out_of_room || this->end - this->head < size)) { this->ran_out_of_room = true; return NULL; } @@ -389,6 +403,13 @@ struct hb_serialize_context_t return this->allocate_size (Type::min_size); } + template + inline Type *start_embed (void) + { + Type *ret = reinterpret_cast (this->head); + return ret; + } + template inline Type *embed (const Type &obj) { @@ -403,8 +424,8 @@ struct hb_serialize_context_t inline Type *extend_min (Type &obj) { unsigned int size = obj.min_size; - assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head); - this->allocate_size (((char *) &obj) + size - this->head); + assert (this->start <= (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head); + if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return NULL; return reinterpret_cast (&obj); } @@ -413,7 +434,7 @@ struct hb_serialize_context_t { unsigned int size = obj.get_size (); assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head); - this->allocate_size (((char *) &obj) + size - this->head); + if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return false; return reinterpret_cast (&obj); } @@ -431,13 +452,6 @@ struct hb_serialize_context_t template struct Supplier { - /* For automatic wrapping of bare arrays */ - inline Supplier (const Type *array) - { - head = array; - len = (unsigned int) -1; - } - inline Supplier (const Type *array, unsigned int len_) { head = array; @@ -449,7 +463,8 @@ struct Supplier return head[i]; } - inline void advance (unsigned int count) { + inline void advance (unsigned int count) + { if (unlikely (count > len)) count = len; len -= count; @@ -457,6 +472,9 @@ struct Supplier } private: + inline Supplier (const Supplier &); /* Disallow copy */ + inline Supplier& operator= (const Supplier &); /* Disallow copy */ + unsigned int len; const Type *head; }; @@ -509,6 +527,8 @@ struct IntType inline operator Type(void) const { return v; } inline bool operator == (const IntType &o) const { return v == o.v; } inline bool operator != (const IntType &o) const { return v != o.v; } + static inline int cmp (const IntType *a, const IntType *b) { return b->cmp (*a); } + inline int cmp (IntType va) const { Type a = va; Type b = v; return a < b ? -1 : a == b ? 0 : +1; } inline int cmp (Type a) const { Type b = v; return a < b ? -1 : a == b ? 0 : +1; } inline bool sanitize (hb_sanitize_context_t *c) { TRACE_SANITIZE (); @@ -634,7 +654,7 @@ struct GenericOffsetTo : OffsetType inline Type& serialize (hb_serialize_context_t *c, void *base) { - Type *t = (Type *) c->head; + Type *t = c->start_embed (); this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */ return *t; } @@ -727,7 +747,7 @@ struct GenericArrayOf TRACE_SERIALIZE (); if (unlikely (!serialize (c, items_len))) return TRACE_RETURN (false); for (unsigned int i = 0; i < items_len; i++) - array[i].set (items[i]); + array[i] = items[i]; items.advance (items_len); return TRACE_RETURN (true); } @@ -842,11 +862,10 @@ struct HeadlessArrayOf TRACE_SERIALIZE (); if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false); len.set (items_len); /* TODO(serialize) Overflow? */ - if (unlikely (!c->extend (*this))) return TRACE_RETURN (false); if (unlikely (!items_len)) return TRACE_RETURN (true); - unsigned int count = items_len; - for (unsigned int i = 1; i < count; i++) - array[i-1].set (items[i]); + if (unlikely (!c->extend (*this))) return TRACE_RETURN (false); + for (unsigned int i = 0; i < items_len - 1; i++) + array[i] = items[i]; items.advance (items_len - 1); return TRACE_RETURN (true); } diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh index 5c51ee57a..f5a067aec 100644 --- a/src/hb-ot-layout-common-private.hh +++ b/src/hb-ot-layout-common-private.hh @@ -382,7 +382,7 @@ struct CoverageFormat1 glyphArray.len.set (num_glyphs); if (unlikely (!c->extend (glyphArray))) return TRACE_RETURN (false); for (unsigned int i = 0; i < num_glyphs; i++) - glyphArray[i].set (glyphs[i]); + glyphArray[i] = glyphs[i]; glyphs.advance (num_glyphs); return TRACE_RETURN (true); } @@ -455,13 +455,14 @@ struct CoverageFormat2 if (unlikely (!c->extend (rangeRecord))) return TRACE_RETURN (false); unsigned int range = 0; - rangeRecord[range].start.set (glyphs[0]); + rangeRecord[range].start = glyphs[0]; rangeRecord[range].value.set (0); for (unsigned int i = 1; i < num_glyphs; i++) if (glyphs[i - 1] + 1 != glyphs[i]) { - rangeRecord[range].start.set (glyphs[i]); - rangeRecord[range].value.set (i); range++; + rangeRecord[range].start = glyphs[i]; + rangeRecord[range].value.set (i); + rangeRecord[range].end = glyphs[i]; } else { rangeRecord[range].end = glyphs[i]; } diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh index bbd7214d0..90faa79c6 100644 --- a/src/hb-ot-layout-gsub-table.hh +++ b/src/hb-ot-layout-gsub-table.hh @@ -143,8 +143,8 @@ struct SingleSubstFormat2 { TRACE_SERIALIZE (); if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false); - if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false); if (unlikely (!substitute.serialize (c, substitutes, num_glyphs))) return TRACE_RETURN (false); + if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false); return TRACE_RETURN (true); } @@ -339,13 +339,13 @@ struct MultipleSubstFormat1 { TRACE_SERIALIZE (); if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false); - if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false); if (unlikely (!sequence.serialize (c, num_glyphs))) return TRACE_RETURN (false); for (unsigned int i = 0; i < num_glyphs; i++) if (unlikely (!sequence[i].serialize (c, this).serialize (c, substitute_glyphs_list, substitute_len_list[i]))) return TRACE_RETURN (false); substitute_len_list.advance (num_glyphs); + if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false); return TRACE_RETURN (true); } @@ -496,13 +496,13 @@ struct AlternateSubstFormat1 { TRACE_SERIALIZE (); if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false); - if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false); if (unlikely (!alternateSet.serialize (c, num_glyphs))) return TRACE_RETURN (false); for (unsigned int i = 0; i < num_glyphs; i++) if (unlikely (!alternateSet[i].serialize (c, this).serialize (c, alternate_glyphs_list, alternate_len_list[i]))) return TRACE_RETURN (false); alternate_len_list.advance (num_glyphs); + if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false); return TRACE_RETURN (true); } @@ -658,7 +658,7 @@ struct Ligature { TRACE_SERIALIZE (); if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false); - ligGlyph.set (ligature); + ligGlyph = ligature; if (unlikely (!component.serialize (c, components, num_components))) return TRACE_RETURN (false); return TRACE_RETURN (true); } @@ -732,6 +732,7 @@ struct LigatureSet ligatures[i], component_list, component_count_list[i]))) return TRACE_RETURN (false); + ligatures.advance (num_ligatures); component_count_list.advance (num_ligatures); return TRACE_RETURN (true); } @@ -798,7 +799,6 @@ struct LigatureSubstFormat1 { TRACE_SERIALIZE (); if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false); - if (unlikely (!coverage.serialize (c, this).serialize (c, first_glyphs, num_first_glyphs))) return TRACE_RETURN (false); if (unlikely (!ligatureSet.serialize (c, num_first_glyphs))) return TRACE_RETURN (false); for (unsigned int i = 0; i < num_first_glyphs; i++) if (unlikely (!ligatureSet[i].serialize (c, this).serialize (c, @@ -807,6 +807,7 @@ struct LigatureSubstFormat1 ligature_per_first_glyph_count_list[i], component_list))) return TRACE_RETURN (false); ligature_per_first_glyph_count_list.advance (num_first_glyphs); + if (unlikely (!coverage.serialize (c, this).serialize (c, first_glyphs, num_first_glyphs))) return TRACE_RETURN (false); return TRACE_RETURN (true); } diff --git a/src/hb-ot-map-private.hh b/src/hb-ot-map-private.hh index 22ea8ba01..7eb85c89e 100644 --- a/src/hb-ot-map-private.hh +++ b/src/hb-ot-map-private.hh @@ -49,6 +49,7 @@ struct hb_ot_map_t unsigned int shift; hb_mask_t mask; hb_mask_t _1_mask; /* mask for value=1, for quick access */ + hb_bool_t needs_fallback; static int cmp (const feature_map_t *a, const feature_map_t *b) { return a->tag < b->tag ? -1 : a->tag > b->tag ? 1 : 0; } @@ -80,6 +81,11 @@ struct hb_ot_map_t return map ? map->mask : 0; } + inline bool needs_fallback (hb_tag_t feature_tag) const { + const feature_map_t *map = features.bsearch (&feature_tag); + return map ? map->needs_fallback : false; + } + inline hb_mask_t get_1_mask (hb_tag_t feature_tag) const { const feature_map_t *map = features.bsearch (&feature_tag); return map ? map->_1_mask : 0; diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc index aae4d0399..f290c98a8 100644 --- a/src/hb-ot-map.cc +++ b/src/hb-ot-map.cc @@ -236,6 +236,7 @@ hb_ot_map_builder_t::compile (hb_face_t *face, m.global_mask |= (info->default_value << map->shift) & map->mask; } map->_1_mask = (1 << map->shift) & map->mask; + map->needs_fallback = !found; } feature_infos.shrink (0); /* Done with these */ diff --git a/src/hb-ot-shape-complex-arabic-fallback.hh b/src/hb-ot-shape-complex-arabic-fallback.hh new file mode 100644 index 000000000..c9e89a61d --- /dev/null +++ b/src/hb-ot-shape-complex-arabic-fallback.hh @@ -0,0 +1,252 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH +#define HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH + +#include "hb-private.hh" + +#include "hb-ot-shape-private.hh" +#include "hb-ot-layout-gsub-table.hh" + + +static const hb_tag_t arabic_fallback_features[] = +{ + HB_TAG('i','n','i','t'), + HB_TAG('m','e','d','i'), + HB_TAG('f','i','n','a'), + HB_TAG('i','s','o','l'), + HB_TAG('r','l','i','g'), +}; + +/* Same order as the fallback feature array */ +enum { + FALLBACK_INIT, + FALLBACK_MEDI, + FALLBACK_FINA, + FALLBACK_ISOL, + FALLBACK_RLIG, + ARABIC_NUM_FALLBACK_FEATURES +}; + +static OT::SubstLookup * +arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + unsigned int feature_index) +{ + OT::GlyphID glyphs[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; + OT::GlyphID substitutes[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; + unsigned int num_glyphs = 0; + + /* Populate arrays */ + for (hb_codepoint_t u = SHAPING_TABLE_FIRST; u < SHAPING_TABLE_LAST + 1; u++) + { + hb_codepoint_t s = shaping_table[u - SHAPING_TABLE_FIRST][feature_index]; + hb_codepoint_t u_glyph, s_glyph; + + if (u == s || + !hb_font_get_glyph (font, u, 0, &u_glyph) || + !hb_font_get_glyph (font, s, 0, &s_glyph) || + u_glyph == s_glyph || + u_glyph > 0xFFFF || s_glyph > 0xFFFF) + continue; + + glyphs[num_glyphs].set (u_glyph); + substitutes[num_glyphs].set (s_glyph); + + num_glyphs++; + } + + /* Bubble-sort! + * May not be good-enough for presidential candidate interviews, but good-enough for us... */ + hb_bubble_sort (&glyphs[0], num_glyphs, OT::GlyphID::cmp, &substitutes[0]); + + OT::Supplier glyphs_supplier (glyphs, num_glyphs); + OT::Supplier substitutes_supplier (substitutes, num_glyphs); + + /* Each glyph takes four bytes max, and there's some overhead. */ + char buf[(SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1) * 4 + 128]; + OT::hb_serialize_context_t c (buf, sizeof (buf)); + OT::SubstLookup *lookup = c.start_serialize (); + bool ret = lookup->serialize_single (&c, + OT::LookupFlag::IgnoreMarks, + glyphs_supplier, + substitutes_supplier, + num_glyphs); + c.end_serialize (); + /* TODO sanitize the results? */ + + return ret ? c.copy () : NULL; +} + +static OT::SubstLookup * +arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan, + hb_font_t *font) +{ + OT::GlyphID first_glyphs[ARRAY_LENGTH_CONST (ligature_table)]; + unsigned int first_glyphs_indirection[ARRAY_LENGTH_CONST (ligature_table)]; + unsigned int ligature_per_first_glyph_count_list[ARRAY_LENGTH_CONST (first_glyphs)]; + unsigned int num_first_glyphs = 0; + + /* We know that all our ligatures are 2-component */ + OT::GlyphID ligatures_list[ARRAY_LENGTH_CONST (first_glyphs) * ARRAY_LENGTH_CONST(ligature_table[0].ligatures)]; + unsigned int component_count_list[ARRAY_LENGTH_CONST (ligatures_list)]; + OT::GlyphID component_list[ARRAY_LENGTH_CONST (ligatures_list) * 1/* One extra component per ligature */]; + unsigned int num_ligatures = 0; + + /* Populate arrays */ + + /* Sort out the first-glyphs */ + for (unsigned int first_glyph_idx = 0; first_glyph_idx < ARRAY_LENGTH (first_glyphs); first_glyph_idx++) + { + hb_codepoint_t first_u = ligature_table[first_glyph_idx].first; + hb_codepoint_t first_glyph; + if (!hb_font_get_glyph (font, first_u, 0, &first_glyph)) + continue; + first_glyphs[num_first_glyphs].set (first_glyph); + ligature_per_first_glyph_count_list[num_first_glyphs] = 0; + first_glyphs_indirection[num_first_glyphs] = first_glyph_idx; + num_first_glyphs++; + } + hb_bubble_sort (&first_glyphs[0], num_first_glyphs, OT::GlyphID::cmp, &first_glyphs_indirection[0]); + + /* Now that the first-glyphs are sorted, walk again, populate ligatures. */ + for (unsigned int i = 0; i < num_first_glyphs; i++) + { + unsigned int first_glyph_idx = first_glyphs_indirection[i]; + + for (unsigned int second_glyph_idx = 0; second_glyph_idx < ARRAY_LENGTH (ligature_table[0].ligatures); second_glyph_idx++) + { + hb_codepoint_t second_u = ligature_table[first_glyph_idx].ligatures[second_glyph_idx].second; + hb_codepoint_t ligature_u = ligature_table[first_glyph_idx].ligatures[second_glyph_idx].ligature; + hb_codepoint_t second_glyph, ligature_glyph; + if (!second_u || + !hb_font_get_glyph (font, second_u, 0, &second_glyph) || + !hb_font_get_glyph (font, ligature_u, 0, &ligature_glyph)) + continue; + + ligature_per_first_glyph_count_list[i]++; + + ligatures_list[num_ligatures].set (ligature_glyph); + component_count_list[num_ligatures] = 2; + component_list[num_ligatures].set (second_glyph); + num_ligatures++; + } + } + + OT::Supplier first_glyphs_supplier (first_glyphs, num_first_glyphs); + OT::Supplier ligature_per_first_glyph_count_supplier (ligature_per_first_glyph_count_list, num_first_glyphs); + OT::Supplier ligatures_supplier (ligatures_list, num_ligatures); + OT::Supplier component_count_supplier (component_count_list, num_ligatures); + OT::Supplier component_supplier (component_list, num_ligatures); + + char buf[2048]; + OT::hb_serialize_context_t c (buf, sizeof (buf)); + OT::SubstLookup *lookup = c.start_serialize (); + bool ret = lookup->serialize_ligature (&c, + OT::LookupFlag::IgnoreMarks, + first_glyphs_supplier, + ligature_per_first_glyph_count_supplier, + num_first_glyphs, + ligatures_supplier, + component_count_supplier, + component_supplier); + + c.end_serialize (); + /* TODO sanitize the results? */ + + return ret ? c.copy () : NULL; +} + +static OT::SubstLookup * +arabic_fallback_synthesize_lookup (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + unsigned int feature_index) +{ + if (feature_index < 4) + return arabic_fallback_synthesize_lookup_single (plan, font, feature_index); + else + return arabic_fallback_synthesize_lookup_ligature (plan, font); +} + +struct arabic_fallback_plan_t +{ + ASSERT_POD (); + + hb_mask_t mask_array[ARABIC_NUM_FALLBACK_FEATURES]; + OT::SubstLookup *lookup_array[ARABIC_NUM_FALLBACK_FEATURES]; + hb_set_digest_t digest_array[ARABIC_NUM_FALLBACK_FEATURES]; +}; + +static const arabic_fallback_plan_t arabic_fallback_plan_nil = {}; + +static arabic_fallback_plan_t * +arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan, + hb_font_t *font) +{ + arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) calloc (1, sizeof (arabic_fallback_plan_t)); + if (unlikely (!fallback_plan)) + return const_cast (&arabic_fallback_plan_nil); + + for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++) { + fallback_plan->mask_array[i] = plan->map.get_1_mask (arabic_fallback_features[i]); + if (fallback_plan->mask_array[i]) { + fallback_plan->lookup_array[i] = arabic_fallback_synthesize_lookup (plan, font, i); + if (fallback_plan->lookup_array[i]) + fallback_plan->lookup_array[i]->add_coverage (&fallback_plan->digest_array[i]); + } + } + + return fallback_plan; +} + +static void +arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan) +{ + if (!fallback_plan || fallback_plan == &arabic_fallback_plan_nil) + return; + + for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++) + if (fallback_plan->lookup_array[i]) + free (fallback_plan->lookup_array[i]); + + free (fallback_plan); +} + +static void +arabic_fallback_plan_shape (arabic_fallback_plan_t *fallback_plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++) + if (fallback_plan->lookup_array[i]) { + OT::hb_apply_context_t c (font, buffer, fallback_plan->mask_array[i]); + fallback_plan->lookup_array[i]->apply_string (&c, &fallback_plan->digest_array[i]); + } +} + + +#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH */ diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc index ba9129bca..56949ddfa 100644 --- a/src/hb-ot-shape-complex-arabic.cc +++ b/src/hb-ot-shape-complex-arabic.cc @@ -26,7 +26,6 @@ #include "hb-ot-shape-complex-private.hh" #include "hb-ot-shape-private.hh" -#include "hb-ot-layout-gsubgpos-private.hh" /* buffer var allocations */ @@ -81,13 +80,6 @@ static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_categ JOINING_TYPE_T : JOINING_TYPE_U; } -static hb_codepoint_t get_arabic_shape (hb_codepoint_t u, unsigned int shape) -{ - if (likely (hb_in_range (u, SHAPING_TABLE_FIRST, SHAPING_TABLE_LAST)) && shape < 4) - return shaping_table[u - SHAPING_TABLE_FIRST][shape]; - return u; -} - static const hb_tag_t arabic_features[] = { HB_TAG('i','n','i','t'), @@ -150,6 +142,10 @@ static const struct arabic_state_table_entry { }; +static void +arabic_fallback_shape (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) @@ -172,12 +168,12 @@ collect_features_arabic (hb_ot_shape_planner_t *plan) map->add_gsub_pause (NULL); for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) - map->add_bool_feature (arabic_features[i], false); + map->add_bool_feature (arabic_features[i], false, i < 4); /* The first four features have fallback. */ map->add_gsub_pause (NULL); - map->add_bool_feature (HB_TAG('r','l','i','g')); - map->add_gsub_pause (NULL); + map->add_bool_feature (HB_TAG('r','l','i','g'), true, true); + map->add_gsub_pause (arabic_fallback_shape); map->add_bool_feature (HB_TAG('c','a','l','t')); map->add_gsub_pause (NULL); @@ -186,16 +182,20 @@ collect_features_arabic (hb_ot_shape_planner_t *plan) map->add_bool_feature (HB_TAG('c','s','w','h')); } +#include "hb-ot-shape-complex-arabic-fallback.hh" + struct arabic_shape_plan_t { ASSERT_POD (); - bool do_fallback; /* The "+ 1" in the next array is to accommodate for the "NONE" command, * which is not an OpenType feature, but this simplifies the code by not * having to do a "if (... < NONE) ..." and just rely on the fact that * mask_array[NONE] == 0. */ hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1]; + + bool do_fallback; + arabic_fallback_plan_t *fallback_plan; }; static void * @@ -205,104 +205,26 @@ data_create_arabic (const hb_ot_shape_plan_t *plan) if (unlikely (!arabic_plan)) return NULL; - hb_mask_t total_masks = 0; + arabic_plan->do_fallback = plan->props.script == HB_SCRIPT_ARABIC; for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) { arabic_plan->mask_array[i] = plan->map.get_1_mask (arabic_features[i]); - total_masks |= arabic_plan->mask_array[i]; + if (i < 4) + arabic_plan->do_fallback = arabic_plan->do_fallback && plan->map.needs_fallback (arabic_features[i]); } - /* Pitfalls: - * - This path fires if user force-set init/medi/fina/isol off, - * - If font does not declare script 'arab', well, what to do? - * Most probably it's safe to assume that init/medi/fina/isol - * still mean Arabic shaping, although they do not have to. - */ - arabic_plan->do_fallback = 0 == total_masks; - return arabic_plan; } static void data_destroy_arabic (void *data) { + arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) data; + + arabic_fallback_plan_destroy (arabic_plan->fallback_plan); + free (data); } -static void -arabic_fallback_shape (hb_font_t *font, hb_buffer_t *buffer) -{ - /* Only Arabic has presentation forms encoded in Unicode. */ - if (buffer->props.script != HB_SCRIPT_ARABIC) - return; - - unsigned int count = buffer->len; - hb_codepoint_t glyph; - - /* Shape to presentation forms */ - for (unsigned int i = 0; i < count; i++) { - hb_codepoint_t u = buffer->info[i].codepoint; - hb_codepoint_t shaped = get_arabic_shape (u, buffer->info[i].arabic_shaping_action()); - if (shaped != u && font->get_glyph (shaped, 0, &glyph)) - buffer->info[i].codepoint = shaped; - } - - OT::hb_apply_context_t c (font, buffer, 1/*global mask*/); - c.set_lookup_props (OT::LookupFlag::IgnoreMarks); - - /* Mandatory ligatures */ - buffer->clear_output (); - for (buffer->idx = 0; buffer->idx + 1 < count;) - { - const unsigned int count = 2; - unsigned int end_offset; - bool is_mark_ligature; - unsigned int total_component_count; - - bool matched = false; - for (unsigned i = 0; i < ARRAY_LENGTH (ligature_table); i++) - { - if (ligature_table[i].first != buffer->cur().codepoint) - continue; - for (unsigned j = 0; j < ARRAY_LENGTH (ligature_table[i].ligatures); j++) - { - OT::USHORT component; - component.set (ligature_table[i].ligatures[j].second); - hb_codepoint_t ligature = ligature_table[i].ligatures[j].ligature; - if (likely (!OT::match_input (&c, count, - &component, - OT::match_glyph, - NULL, - &end_offset, - &is_mark_ligature, - &total_component_count) || - !(font->get_glyph (ligature, 0, &glyph)))) - continue; - - /* Deal, we are forming the ligature. */ - buffer->merge_clusters (buffer->idx, buffer->idx + end_offset); - - OT::ligate_input (&c, - count, - &component, - ligature, - OT::match_glyph, - NULL, - is_mark_ligature, - total_component_count); - matched = true; - break; - } - if (matched) - break; - } - if (!matched) - buffer->next_glyph (); - } - for (; buffer->idx < count;) - buffer->next_glyph (); - buffer->swap_buffers (); -} - static void arabic_joining (hb_buffer_t *buffer) { @@ -334,20 +256,6 @@ arabic_joining (hb_buffer_t *buffer) HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); } -static void -preprocess_text_arabic (const hb_ot_shape_plan_t *plan, - hb_buffer_t *buffer, - hb_font_t *font) -{ - const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; - - if (unlikely (arabic_plan->do_fallback)) - { - arabic_joining (buffer); - arabic_fallback_shape (font, buffer); - } -} - static void setup_masks_arabic (const hb_ot_shape_plan_t *plan, hb_buffer_t *buffer, @@ -355,15 +263,39 @@ setup_masks_arabic (const hb_ot_shape_plan_t *plan, { const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; - if (likely (!arabic_plan->do_fallback)) - { - arabic_joining (buffer); - unsigned int count = buffer->len; - for (unsigned int i = 0; i < count; i++) - buffer->info[i].mask |= arabic_plan->mask_array[buffer->info[i].arabic_shaping_action()]; - } + arabic_joining (buffer); + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + buffer->info[i].mask |= arabic_plan->mask_array[buffer->info[i].arabic_shaping_action()]; } + +static void +arabic_fallback_shape (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->do_fallback) + return; + +retry: + arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) hb_atomic_ptr_get (&arabic_plan->fallback_plan); + if (unlikely (!fallback_plan)) + { + /* This sucks. We need a font to build the fallback plan... */ + fallback_plan = arabic_fallback_plan_create (plan, font); + if (unlikely (!hb_atomic_ptr_cmpexch (&(const_cast (arabic_plan))->fallback_plan, NULL, fallback_plan))) { + arabic_fallback_plan_destroy (fallback_plan); + goto retry; + } + } + + arabic_fallback_plan_shape (fallback_plan, font, buffer); +} + + const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic = { "arabic", @@ -371,7 +303,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic = NULL, /* override_features */ data_create_arabic, data_destroy_arabic, - preprocess_text_arabic, + NULL, /* preprocess_text_arabic */ NULL, /* normalization_preference */ setup_masks_arabic, true, /* zero_width_attached_marks */ diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc index a19c8b277..1dd8b0a6e 100644 --- a/src/hb-ot-shape.cc +++ b/src/hb-ot-shape.cc @@ -360,10 +360,8 @@ hb_synthesize_glyph_classes (hb_ot_shape_context_t *c) static inline void hb_ot_substitute_default (hb_ot_shape_context_t *c) { - if (c->plan->shaper->preprocess_text) { - hb_synthesize_glyph_classes (c); /* XXX This is a hack for now. */ + if (c->plan->shaper->preprocess_text) c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font); - } hb_ot_mirror_chars (c); @@ -393,8 +391,7 @@ hb_ot_substitute_complex (hb_ot_shape_context_t *c) if (!hb_ot_layout_has_glyph_classes (c->face)) hb_synthesize_glyph_classes (c); - if (hb_ot_layout_has_substitution (c->face)) - c->plan->substitute (c->font, c->buffer); + c->plan->substitute (c->font, c->buffer); hb_ot_layout_substitute_finish (c->font, c->buffer); diff --git a/src/hb-private.hh b/src/hb-private.hh index 865502051..43b6e8a4c 100644 --- a/src/hb-private.hh +++ b/src/hb-private.hh @@ -77,6 +77,8 @@ static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; } #undef ARRAY_LENGTH template static inline unsigned int ARRAY_LENGTH (const Type (&a)[n]) { return n; } +/* A const version, but does not detect erratically being called on pointers. */ +#define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0]))) #define HB_STMT_START do #define HB_STMT_END while (0) @@ -581,12 +583,13 @@ _hb_debug_msg_va (const char *what, fprintf (stderr, " " VRBAR LBAR); if (func) { - /* If there's a class name, just write that. */ - const char *dotdot = strstr (func, "::"); + /* Skip return type */ const char *space = strchr (func, ' '); - if (space && dotdot && space < dotdot) + if (space) func = space + 1; - unsigned int func_len = dotdot ? dotdot - func : strlen (func); + /* Skip parameter list */ + const char *paren = strchr (func, '('); + unsigned int func_len = paren ? paren - func : strlen (func); fprintf (stderr, "%.*s: ", func_len, func); }