[HarfBuzz] harfbuzz: Branch 'master'
Behdad Esfahbod
behdad at kemper.freedesktop.org
Wed Sep 5 21:51:57 PDT 2012
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 | 158 +++++-------------
src/hb-ot-shape.cc | 7
src/hb-private.hh | 11 -
10 files changed, 369 insertions(+), 156 deletions(-)
New commits:
commit fabd3113a98c5f4114f48920fa7ea38bd65a8d32
Author: Behdad Esfahbod <behdad at behdad.org>
Date: Wed Sep 5 22:19:28 2012 -0400
[OT] Port Arabic fallback shaping to synthetic GSUB
All of init/medi/fina/isol and rlig implemented.
Let there be dragons... ⻯
diff --git a/src/Makefile.am b/src/Makefile.am
index d7c4560..609635c 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 c273a97..7106a2e 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 <typename Type>
+ 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<Type> ();
}
- 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 <typename Type>
+ 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<Type *> (p);
}
template <typename Type>
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;
}
@@ -390,6 +404,13 @@ struct hb_serialize_context_t
}
template <typename Type>
+ inline Type *start_embed (void)
+ {
+ Type *ret = reinterpret_cast<Type *> (this->head);
+ return ret;
+ }
+
+ template <typename Type>
inline Type *embed (const Type &obj)
{
unsigned int size = obj.get_size ();
@@ -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<Type> (((char *) &obj) + size - this->head);
+ assert (this->start <= (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
+ if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return NULL;
return reinterpret_cast<Type *> (&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<Type> (((char *) &obj) + size - this->head);
+ if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return false;
return reinterpret_cast<Type *> (&obj);
}
@@ -431,13 +452,6 @@ struct hb_serialize_context_t
template <typename Type>
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<Type> &); /* Disallow copy */
+ inline Supplier<Type>& operator= (const Supplier<Type> &); /* 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<Type> &o) const { return v == o.v; }
inline bool operator != (const IntType<Type> &o) const { return v != o.v; }
+ static inline int cmp (const IntType<Type> *a, const IntType<Type> *b) { return b->cmp (*a); }
+ inline int cmp (IntType<Type> 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<Type> ();
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 5c51ee5..f5a067a 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 bbd7214..90faa79 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 22ea8ba..7eb85c8 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 aae4d03..f290c98 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 0000000..c9e89a6
--- /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<OT::GlyphID> glyphs_supplier (glyphs, num_glyphs);
+ OT::Supplier<OT::GlyphID> 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<OT::SubstLookup> ();
+ 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<OT::SubstLookup> () : 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<OT::GlyphID> first_glyphs_supplier (first_glyphs, num_first_glyphs);
+ OT::Supplier<unsigned int > ligature_per_first_glyph_count_supplier (ligature_per_first_glyph_count_list, num_first_glyphs);
+ OT::Supplier<OT::GlyphID> ligatures_supplier (ligatures_list, num_ligatures);
+ OT::Supplier<unsigned int > component_count_supplier (component_count_list, num_ligatures);
+ OT::Supplier<OT::GlyphID> component_supplier (component_list, num_ligatures);
+
+ char buf[2048];
+ OT::hb_serialize_context_t c (buf, sizeof (buf));
+ OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> ();
+ 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<OT::SubstLookup> () : 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_t *> (&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 ba9129b..56949dd 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<hb_codepoint_t> (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,102 +205,24 @@ 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)
{
- 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;
- }
+ arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) data;
- OT::hb_apply_context_t c (font, buffer, 1/*global mask*/);
- c.set_lookup_props (OT::LookupFlag::IgnoreMarks);
+ arabic_fallback_plan_destroy (arabic_plan->fallback_plan);
- /* 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 ();
+ free (data);
}
static void
@@ -335,35 +257,45 @@ arabic_joining (hb_buffer_t *buffer)
}
static void
-preprocess_text_arabic (const hb_ot_shape_plan_t *plan,
- hb_buffer_t *buffer,
- hb_font_t *font)
+setup_masks_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);
- }
+ 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
-setup_masks_arabic (const hb_ot_shape_plan_t *plan,
- hb_buffer_t *buffer,
- hb_font_t *font)
+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 (likely (!arabic_plan->do_fallback))
+ 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))
{
- 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()];
+ /* 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_shape_plan_t *> (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 a19c8b2..1dd8b0a 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 8655020..43b6e8a 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 <typename Type, unsigned int n>
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);
}
More information about the HarfBuzz
mailing list