[OT] Port Arabic fallback shaping to synthetic GSUB

All of init/medi/fina/isol and rlig implemented.

Let there be dragons... ⻯
This commit is contained in:
Behdad Esfahbod 2012-09-05 22:19:28 -04:00
parent f0b8ed1b6d
commit fabd3113a9
10 changed files with 375 additions and 162 deletions

View File

@ -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 \

View File

@ -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;
}
@ -389,6 +403,13 @@ struct hb_serialize_context_t
return this->allocate_size<Type> (Type::min_size);
}
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)
{
@ -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);
}

View File

@ -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];
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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 */

View File

@ -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 */

View File

@ -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,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_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 */

View File

@ -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);

View File

@ -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);
}