[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-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 \
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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-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 */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue