[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:
parent
f0b8ed1b6d
commit
fabd3113a9
|
@ -78,6 +78,7 @@ HBSOURCES += \
|
||||||
hb-ot-map-private.hh \
|
hb-ot-map-private.hh \
|
||||||
hb-ot-shape.cc \
|
hb-ot-shape.cc \
|
||||||
hb-ot-shape-complex-arabic.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-arabic-table.hh \
|
||||||
hb-ot-shape-complex-indic.cc \
|
hb-ot-shape-complex-indic.cc \
|
||||||
hb-ot-shape-complex-indic-machine.hh \
|
hb-ot-shape-complex-indic-machine.hh \
|
||||||
|
|
|
@ -342,38 +342,52 @@ struct Sanitizer
|
||||||
|
|
||||||
struct hb_serialize_context_t
|
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->start = (char *) start;
|
||||||
this->end = this->start + size;
|
this->end = this->start + size;
|
||||||
}
|
|
||||||
|
|
||||||
inline void start_processing (void)
|
|
||||||
{
|
|
||||||
this->ran_out_of_room = false;
|
this->ran_out_of_room = false;
|
||||||
this->head = this->start;
|
this->head = this->start;
|
||||||
this->debug_depth = 0;
|
this->debug_depth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Type>
|
||||||
|
inline Type *start_serialize (void)
|
||||||
|
{
|
||||||
DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1,
|
DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1,
|
||||||
"start [%p..%p] (%lu bytes)",
|
"start [%p..%p] (%lu bytes)",
|
||||||
this->start, this->end,
|
this->start, this->end,
|
||||||
(unsigned long) (this->end - this->start));
|
(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,
|
DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1,
|
||||||
"end [%p..%p] %s",
|
"end [%p..%p] serialized %d bytes; %s",
|
||||||
this->start, this->end,
|
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->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>
|
template <typename Type>
|
||||||
inline Type *allocate_size (unsigned int size)
|
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;
|
this->ran_out_of_room = true;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -389,6 +403,13 @@ struct hb_serialize_context_t
|
||||||
return this->allocate_size<Type> (Type::min_size);
|
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>
|
template <typename Type>
|
||||||
inline Type *embed (const Type &obj)
|
inline Type *embed (const Type &obj)
|
||||||
{
|
{
|
||||||
|
@ -403,8 +424,8 @@ struct hb_serialize_context_t
|
||||||
inline Type *extend_min (Type &obj)
|
inline Type *extend_min (Type &obj)
|
||||||
{
|
{
|
||||||
unsigned int size = obj.min_size;
|
unsigned int size = obj.min_size;
|
||||||
assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
|
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 NULL;
|
||||||
return reinterpret_cast<Type *> (&obj);
|
return reinterpret_cast<Type *> (&obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,7 +434,7 @@ struct hb_serialize_context_t
|
||||||
{
|
{
|
||||||
unsigned int size = obj.get_size ();
|
unsigned int size = obj.get_size ();
|
||||||
assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
|
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);
|
return reinterpret_cast<Type *> (&obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,13 +452,6 @@ struct hb_serialize_context_t
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
struct Supplier
|
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_)
|
inline Supplier (const Type *array, unsigned int len_)
|
||||||
{
|
{
|
||||||
head = array;
|
head = array;
|
||||||
|
@ -449,7 +463,8 @@ struct Supplier
|
||||||
return head[i];
|
return head[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void advance (unsigned int count) {
|
inline void advance (unsigned int count)
|
||||||
|
{
|
||||||
if (unlikely (count > len))
|
if (unlikely (count > len))
|
||||||
count = len;
|
count = len;
|
||||||
len -= count;
|
len -= count;
|
||||||
|
@ -457,6 +472,9 @@ struct Supplier
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
inline Supplier (const Supplier<Type> &); /* Disallow copy */
|
||||||
|
inline Supplier<Type>& operator= (const Supplier<Type> &); /* Disallow copy */
|
||||||
|
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
const Type *head;
|
const Type *head;
|
||||||
};
|
};
|
||||||
|
@ -509,6 +527,8 @@ struct IntType
|
||||||
inline operator Type(void) const { return v; }
|
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; }
|
||||||
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 int cmp (Type a) const { Type b = v; return a < b ? -1 : a == b ? 0 : +1; }
|
||||||
inline bool sanitize (hb_sanitize_context_t *c) {
|
inline bool sanitize (hb_sanitize_context_t *c) {
|
||||||
TRACE_SANITIZE ();
|
TRACE_SANITIZE ();
|
||||||
|
@ -634,7 +654,7 @@ struct GenericOffsetTo : OffsetType
|
||||||
|
|
||||||
inline Type& serialize (hb_serialize_context_t *c, void *base)
|
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? */
|
this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */
|
||||||
return *t;
|
return *t;
|
||||||
}
|
}
|
||||||
|
@ -727,7 +747,7 @@ struct GenericArrayOf
|
||||||
TRACE_SERIALIZE ();
|
TRACE_SERIALIZE ();
|
||||||
if (unlikely (!serialize (c, items_len))) return TRACE_RETURN (false);
|
if (unlikely (!serialize (c, items_len))) return TRACE_RETURN (false);
|
||||||
for (unsigned int i = 0; i < items_len; i++)
|
for (unsigned int i = 0; i < items_len; i++)
|
||||||
array[i].set (items[i]);
|
array[i] = items[i];
|
||||||
items.advance (items_len);
|
items.advance (items_len);
|
||||||
return TRACE_RETURN (true);
|
return TRACE_RETURN (true);
|
||||||
}
|
}
|
||||||
|
@ -842,11 +862,10 @@ struct HeadlessArrayOf
|
||||||
TRACE_SERIALIZE ();
|
TRACE_SERIALIZE ();
|
||||||
if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
|
if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
|
||||||
len.set (items_len); /* TODO(serialize) Overflow? */
|
len.set (items_len); /* TODO(serialize) Overflow? */
|
||||||
if (unlikely (!c->extend (*this))) return TRACE_RETURN (false);
|
|
||||||
if (unlikely (!items_len)) return TRACE_RETURN (true);
|
if (unlikely (!items_len)) return TRACE_RETURN (true);
|
||||||
unsigned int count = items_len;
|
if (unlikely (!c->extend (*this))) return TRACE_RETURN (false);
|
||||||
for (unsigned int i = 1; i < count; i++)
|
for (unsigned int i = 0; i < items_len - 1; i++)
|
||||||
array[i-1].set (items[i]);
|
array[i] = items[i];
|
||||||
items.advance (items_len - 1);
|
items.advance (items_len - 1);
|
||||||
return TRACE_RETURN (true);
|
return TRACE_RETURN (true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -382,7 +382,7 @@ struct CoverageFormat1
|
||||||
glyphArray.len.set (num_glyphs);
|
glyphArray.len.set (num_glyphs);
|
||||||
if (unlikely (!c->extend (glyphArray))) return TRACE_RETURN (false);
|
if (unlikely (!c->extend (glyphArray))) return TRACE_RETURN (false);
|
||||||
for (unsigned int i = 0; i < num_glyphs; i++)
|
for (unsigned int i = 0; i < num_glyphs; i++)
|
||||||
glyphArray[i].set (glyphs[i]);
|
glyphArray[i] = glyphs[i];
|
||||||
glyphs.advance (num_glyphs);
|
glyphs.advance (num_glyphs);
|
||||||
return TRACE_RETURN (true);
|
return TRACE_RETURN (true);
|
||||||
}
|
}
|
||||||
|
@ -455,13 +455,14 @@ struct CoverageFormat2
|
||||||
if (unlikely (!c->extend (rangeRecord))) return TRACE_RETURN (false);
|
if (unlikely (!c->extend (rangeRecord))) return TRACE_RETURN (false);
|
||||||
|
|
||||||
unsigned int range = 0;
|
unsigned int range = 0;
|
||||||
rangeRecord[range].start.set (glyphs[0]);
|
rangeRecord[range].start = glyphs[0];
|
||||||
rangeRecord[range].value.set (0);
|
rangeRecord[range].value.set (0);
|
||||||
for (unsigned int i = 1; i < num_glyphs; i++)
|
for (unsigned int i = 1; i < num_glyphs; i++)
|
||||||
if (glyphs[i - 1] + 1 != glyphs[i]) {
|
if (glyphs[i - 1] + 1 != glyphs[i]) {
|
||||||
rangeRecord[range].start.set (glyphs[i]);
|
|
||||||
rangeRecord[range].value.set (i);
|
|
||||||
range++;
|
range++;
|
||||||
|
rangeRecord[range].start = glyphs[i];
|
||||||
|
rangeRecord[range].value.set (i);
|
||||||
|
rangeRecord[range].end = glyphs[i];
|
||||||
} else {
|
} else {
|
||||||
rangeRecord[range].end = glyphs[i];
|
rangeRecord[range].end = glyphs[i];
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,8 +143,8 @@ struct SingleSubstFormat2
|
||||||
{
|
{
|
||||||
TRACE_SERIALIZE ();
|
TRACE_SERIALIZE ();
|
||||||
if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
|
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 (!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);
|
return TRACE_RETURN (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,13 +339,13 @@ struct MultipleSubstFormat1
|
||||||
{
|
{
|
||||||
TRACE_SERIALIZE ();
|
TRACE_SERIALIZE ();
|
||||||
if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
|
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);
|
if (unlikely (!sequence.serialize (c, num_glyphs))) return TRACE_RETURN (false);
|
||||||
for (unsigned int i = 0; i < num_glyphs; i++)
|
for (unsigned int i = 0; i < num_glyphs; i++)
|
||||||
if (unlikely (!sequence[i].serialize (c, this).serialize (c,
|
if (unlikely (!sequence[i].serialize (c, this).serialize (c,
|
||||||
substitute_glyphs_list,
|
substitute_glyphs_list,
|
||||||
substitute_len_list[i]))) return TRACE_RETURN (false);
|
substitute_len_list[i]))) return TRACE_RETURN (false);
|
||||||
substitute_len_list.advance (num_glyphs);
|
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);
|
return TRACE_RETURN (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,13 +496,13 @@ struct AlternateSubstFormat1
|
||||||
{
|
{
|
||||||
TRACE_SERIALIZE ();
|
TRACE_SERIALIZE ();
|
||||||
if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
|
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);
|
if (unlikely (!alternateSet.serialize (c, num_glyphs))) return TRACE_RETURN (false);
|
||||||
for (unsigned int i = 0; i < num_glyphs; i++)
|
for (unsigned int i = 0; i < num_glyphs; i++)
|
||||||
if (unlikely (!alternateSet[i].serialize (c, this).serialize (c,
|
if (unlikely (!alternateSet[i].serialize (c, this).serialize (c,
|
||||||
alternate_glyphs_list,
|
alternate_glyphs_list,
|
||||||
alternate_len_list[i]))) return TRACE_RETURN (false);
|
alternate_len_list[i]))) return TRACE_RETURN (false);
|
||||||
alternate_len_list.advance (num_glyphs);
|
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);
|
return TRACE_RETURN (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -658,7 +658,7 @@ struct Ligature
|
||||||
{
|
{
|
||||||
TRACE_SERIALIZE ();
|
TRACE_SERIALIZE ();
|
||||||
if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
|
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);
|
if (unlikely (!component.serialize (c, components, num_components))) return TRACE_RETURN (false);
|
||||||
return TRACE_RETURN (true);
|
return TRACE_RETURN (true);
|
||||||
}
|
}
|
||||||
|
@ -732,6 +732,7 @@ struct LigatureSet
|
||||||
ligatures[i],
|
ligatures[i],
|
||||||
component_list,
|
component_list,
|
||||||
component_count_list[i]))) return TRACE_RETURN (false);
|
component_count_list[i]))) return TRACE_RETURN (false);
|
||||||
|
ligatures.advance (num_ligatures);
|
||||||
component_count_list.advance (num_ligatures);
|
component_count_list.advance (num_ligatures);
|
||||||
return TRACE_RETURN (true);
|
return TRACE_RETURN (true);
|
||||||
}
|
}
|
||||||
|
@ -798,7 +799,6 @@ struct LigatureSubstFormat1
|
||||||
{
|
{
|
||||||
TRACE_SERIALIZE ();
|
TRACE_SERIALIZE ();
|
||||||
if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
|
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);
|
if (unlikely (!ligatureSet.serialize (c, num_first_glyphs))) return TRACE_RETURN (false);
|
||||||
for (unsigned int i = 0; i < num_first_glyphs; i++)
|
for (unsigned int i = 0; i < num_first_glyphs; i++)
|
||||||
if (unlikely (!ligatureSet[i].serialize (c, this).serialize (c,
|
if (unlikely (!ligatureSet[i].serialize (c, this).serialize (c,
|
||||||
|
@ -807,6 +807,7 @@ struct LigatureSubstFormat1
|
||||||
ligature_per_first_glyph_count_list[i],
|
ligature_per_first_glyph_count_list[i],
|
||||||
component_list))) return TRACE_RETURN (false);
|
component_list))) return TRACE_RETURN (false);
|
||||||
ligature_per_first_glyph_count_list.advance (num_first_glyphs);
|
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);
|
return TRACE_RETURN (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ struct hb_ot_map_t
|
||||||
unsigned int shift;
|
unsigned int shift;
|
||||||
hb_mask_t mask;
|
hb_mask_t mask;
|
||||||
hb_mask_t _1_mask; /* mask for value=1, for quick access */
|
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)
|
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; }
|
{ 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;
|
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 {
|
inline hb_mask_t get_1_mask (hb_tag_t feature_tag) const {
|
||||||
const feature_map_t *map = features.bsearch (&feature_tag);
|
const feature_map_t *map = features.bsearch (&feature_tag);
|
||||||
return map ? map->_1_mask : 0;
|
return map ? map->_1_mask : 0;
|
||||||
|
|
|
@ -236,6 +236,7 @@ hb_ot_map_builder_t::compile (hb_face_t *face,
|
||||||
m.global_mask |= (info->default_value << map->shift) & map->mask;
|
m.global_mask |= (info->default_value << map->shift) & map->mask;
|
||||||
}
|
}
|
||||||
map->_1_mask = (1 << map->shift) & map->mask;
|
map->_1_mask = (1 << map->shift) & map->mask;
|
||||||
|
map->needs_fallback = !found;
|
||||||
|
|
||||||
}
|
}
|
||||||
feature_infos.shrink (0); /* Done with these */
|
feature_infos.shrink (0); /* Done with these */
|
||||||
|
|
|
@ -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 */
|
|
@ -26,7 +26,6 @@
|
||||||
|
|
||||||
#include "hb-ot-shape-complex-private.hh"
|
#include "hb-ot-shape-complex-private.hh"
|
||||||
#include "hb-ot-shape-private.hh"
|
#include "hb-ot-shape-private.hh"
|
||||||
#include "hb-ot-layout-gsubgpos-private.hh"
|
|
||||||
|
|
||||||
|
|
||||||
/* buffer var allocations */
|
/* 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;
|
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[] =
|
static const hb_tag_t arabic_features[] =
|
||||||
{
|
{
|
||||||
HB_TAG('i','n','i','t'),
|
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
|
static void
|
||||||
collect_features_arabic (hb_ot_shape_planner_t *plan)
|
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);
|
map->add_gsub_pause (NULL);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++)
|
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_gsub_pause (NULL);
|
||||||
|
|
||||||
map->add_bool_feature (HB_TAG('r','l','i','g'));
|
map->add_bool_feature (HB_TAG('r','l','i','g'), true, true);
|
||||||
map->add_gsub_pause (NULL);
|
map->add_gsub_pause (arabic_fallback_shape);
|
||||||
|
|
||||||
map->add_bool_feature (HB_TAG('c','a','l','t'));
|
map->add_bool_feature (HB_TAG('c','a','l','t'));
|
||||||
map->add_gsub_pause (NULL);
|
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'));
|
map->add_bool_feature (HB_TAG('c','s','w','h'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "hb-ot-shape-complex-arabic-fallback.hh"
|
||||||
|
|
||||||
struct arabic_shape_plan_t
|
struct arabic_shape_plan_t
|
||||||
{
|
{
|
||||||
ASSERT_POD ();
|
ASSERT_POD ();
|
||||||
|
|
||||||
bool do_fallback;
|
|
||||||
/* The "+ 1" in the next array is to accommodate for the "NONE" command,
|
/* 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
|
* 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
|
* having to do a "if (... < NONE) ..." and just rely on the fact that
|
||||||
* mask_array[NONE] == 0. */
|
* mask_array[NONE] == 0. */
|
||||||
hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1];
|
hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1];
|
||||||
|
|
||||||
|
bool do_fallback;
|
||||||
|
arabic_fallback_plan_t *fallback_plan;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
|
@ -205,104 +205,26 @@ data_create_arabic (const hb_ot_shape_plan_t *plan)
|
||||||
if (unlikely (!arabic_plan))
|
if (unlikely (!arabic_plan))
|
||||||
return NULL;
|
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++) {
|
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->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;
|
return arabic_plan;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
data_destroy_arabic (void *data)
|
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);
|
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
|
static void
|
||||||
arabic_joining (hb_buffer_t *buffer)
|
arabic_joining (hb_buffer_t *buffer)
|
||||||
{
|
{
|
||||||
|
@ -334,20 +256,6 @@ arabic_joining (hb_buffer_t *buffer)
|
||||||
HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
|
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
|
static void
|
||||||
setup_masks_arabic (const hb_ot_shape_plan_t *plan,
|
setup_masks_arabic (const hb_ot_shape_plan_t *plan,
|
||||||
hb_buffer_t *buffer,
|
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;
|
const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
|
||||||
|
|
||||||
if (likely (!arabic_plan->do_fallback))
|
|
||||||
{
|
|
||||||
arabic_joining (buffer);
|
arabic_joining (buffer);
|
||||||
unsigned int count = buffer->len;
|
unsigned int count = buffer->len;
|
||||||
for (unsigned int i = 0; i < count; i++)
|
for (unsigned int i = 0; i < count; i++)
|
||||||
buffer->info[i].mask |= arabic_plan->mask_array[buffer->info[i].arabic_shaping_action()];
|
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 =
|
const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic =
|
||||||
{
|
{
|
||||||
"arabic",
|
"arabic",
|
||||||
|
@ -371,7 +303,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic =
|
||||||
NULL, /* override_features */
|
NULL, /* override_features */
|
||||||
data_create_arabic,
|
data_create_arabic,
|
||||||
data_destroy_arabic,
|
data_destroy_arabic,
|
||||||
preprocess_text_arabic,
|
NULL, /* preprocess_text_arabic */
|
||||||
NULL, /* normalization_preference */
|
NULL, /* normalization_preference */
|
||||||
setup_masks_arabic,
|
setup_masks_arabic,
|
||||||
true, /* zero_width_attached_marks */
|
true, /* zero_width_attached_marks */
|
||||||
|
|
|
@ -360,10 +360,8 @@ hb_synthesize_glyph_classes (hb_ot_shape_context_t *c)
|
||||||
static inline void
|
static inline void
|
||||||
hb_ot_substitute_default (hb_ot_shape_context_t *c)
|
hb_ot_substitute_default (hb_ot_shape_context_t *c)
|
||||||
{
|
{
|
||||||
if (c->plan->shaper->preprocess_text) {
|
if (c->plan->shaper->preprocess_text)
|
||||||
hb_synthesize_glyph_classes (c); /* XXX This is a hack for now. */
|
|
||||||
c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font);
|
c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font);
|
||||||
}
|
|
||||||
|
|
||||||
hb_ot_mirror_chars (c);
|
hb_ot_mirror_chars (c);
|
||||||
|
|
||||||
|
@ -393,7 +391,6 @@ hb_ot_substitute_complex (hb_ot_shape_context_t *c)
|
||||||
if (!hb_ot_layout_has_glyph_classes (c->face))
|
if (!hb_ot_layout_has_glyph_classes (c->face))
|
||||||
hb_synthesize_glyph_classes (c);
|
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);
|
hb_ot_layout_substitute_finish (c->font, c->buffer);
|
||||||
|
|
|
@ -77,6 +77,8 @@ static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; }
|
||||||
#undef ARRAY_LENGTH
|
#undef ARRAY_LENGTH
|
||||||
template <typename Type, unsigned int n>
|
template <typename Type, unsigned int n>
|
||||||
static inline unsigned int ARRAY_LENGTH (const Type (&a)[n]) { return 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_START do
|
||||||
#define HB_STMT_END while (0)
|
#define HB_STMT_END while (0)
|
||||||
|
@ -581,12 +583,13 @@ _hb_debug_msg_va (const char *what,
|
||||||
fprintf (stderr, " " VRBAR LBAR);
|
fprintf (stderr, " " VRBAR LBAR);
|
||||||
|
|
||||||
if (func) {
|
if (func) {
|
||||||
/* If there's a class name, just write that. */
|
/* Skip return type */
|
||||||
const char *dotdot = strstr (func, "::");
|
|
||||||
const char *space = strchr (func, ' ');
|
const char *space = strchr (func, ' ');
|
||||||
if (space && dotdot && space < dotdot)
|
if (space)
|
||||||
func = space + 1;
|
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);
|
fprintf (stderr, "%.*s: ", func_len, func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue