#ifndef HB_OT_LAYOUT_COMMON_PRIVATE_HH
#define HB_OT_LAYOUT_COMMON_PRIVATE_HH
#include "hb-ot-layout-private.hh"
#include "hb-open-type-private.hh"
#include "hb-set-private.hh"
#ifndef HB_MAX_NESTING_LEVEL
#define HB_MAX_NESTING_LEVEL 6
#endif
#ifndef HB_MAX_CONTEXT_LENGTH
#define HB_MAX_CONTEXT_LENGTH 64
#endif
namespace OT {
#define TRACE_DISPATCH(this, format) \
hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \
(&c->debug_depth, c->get_name (), this, HB_FUNC, \
"format %d", (int) format);
#define NOT_COVERED ((unsigned int) -1)
template <typename Type>
struct Record
{
inline int cmp (hb_tag_t a) const {
return tag.cmp (a);
}
struct sanitize_closure_t {
hb_tag_t tag;
const void *list_base;
};
inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
const sanitize_closure_t closure = {tag, base};
return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure));
}
Tag tag;
OffsetTo<Type>
offset;
public:
DEFINE_SIZE_STATIC (6);
};
template <typename Type>
struct RecordArrayOf : SortedArrayOf<Record<Type> > {
inline const Tag& get_tag (unsigned int i) const
{
if (unlikely (i >= this->len)) return Null(Tag);
return (*this)[i].tag;
}
inline unsigned int get_tags (unsigned int start_offset,
unsigned int *record_count ,
hb_tag_t *record_tags ) const
{
if (record_count) {
const Record<Type> *arr = this->sub_array (start_offset, record_count);
unsigned int count = *record_count;
for (unsigned int i = 0; i < count; i++)
record_tags[i] = arr[i].tag;
}
return this->len;
}
inline bool find_index (hb_tag_t tag, unsigned int *index) const
{
int i = this->bsearch (tag);
if (i != -1) {
if (index) *index = i;
return true;
} else {
if (index) *index = Index::NOT_FOUND_INDEX;
return false;
}
}
};
template <typename Type>
struct RecordListOf : RecordArrayOf<Type>
{
inline const Type& operator [] (unsigned int i) const
{ return this+RecordArrayOf<Type>::operator [](i).offset; }
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (RecordArrayOf<Type>::sanitize (c, this));
}
};
struct RangeRecord
{
inline int cmp (hb_codepoint_t g) const {
return g < start ? -1 : g <= end ? 0 : +1 ;
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
}
inline bool intersects (const hb_set_t *glyphs) const {
return glyphs->intersects (start, end);
}
template <typename set_t>
inline void add_coverage (set_t *glyphs) const {
glyphs->add_range (start, end);
}
GlyphID start;
GlyphID end;
USHORT value;
public:
DEFINE_SIZE_STATIC (6);
};
DEFINE_NULL_DATA (RangeRecord, "\000\001");
struct IndexArray : ArrayOf<Index>
{
inline unsigned int get_indexes (unsigned int start_offset,
unsigned int *_count ,
unsigned int *_indexes ) const
{
if (_count) {
const USHORT *arr = this->sub_array (start_offset, _count);
unsigned int count = *_count;
for (unsigned int i = 0; i < count; i++)
_indexes[i] = arr[i];
}
return this->len;
}
};
struct Script;
struct LangSys;
struct Feature;
struct LangSys
{
inline unsigned int get_feature_count (void) const
{ return featureIndex.len; }
inline hb_tag_t get_feature_index (unsigned int i) const
{ return featureIndex[i]; }
inline unsigned int get_feature_indexes (unsigned int start_offset,
unsigned int *feature_count ,
unsigned int *feature_indexes ) const
{ return featureIndex.get_indexes (start_offset, feature_count, feature_indexes); }
inline bool has_required_feature (void) const { return reqFeatureIndex != 0xFFFFu; }
inline unsigned int get_required_feature_index (void) const
{
if (reqFeatureIndex == 0xFFFFu)
return Index::NOT_FOUND_INDEX;
return reqFeatureIndex;;
}
inline bool sanitize (hb_sanitize_context_t *c,
const Record<LangSys>::sanitize_closure_t * = NULL) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) && featureIndex.sanitize (c));
}
Offset<> lookupOrderZ;
USHORT reqFeatureIndex;
IndexArray featureIndex;
public:
DEFINE_SIZE_ARRAY (6, featureIndex);
};
DEFINE_NULL_DATA (LangSys, "\0\0\xFF\xFF");
struct Script
{
inline unsigned int get_lang_sys_count (void) const
{ return langSys.len; }
inline const Tag& get_lang_sys_tag (unsigned int i) const
{ return langSys.get_tag (i); }
inline unsigned int get_lang_sys_tags (unsigned int start_offset,
unsigned int *lang_sys_count ,
hb_tag_t *lang_sys_tags ) const
{ return langSys.get_tags (start_offset, lang_sys_count, lang_sys_tags); }
inline const LangSys& get_lang_sys (unsigned int i) const
{
if (i == Index::NOT_FOUND_INDEX) return get_default_lang_sys ();
return this+langSys[i].offset;
}
inline bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const
{ return langSys.find_index (tag, index); }
inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; }
inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; }
inline bool sanitize (hb_sanitize_context_t *c,
const Record<Script>::sanitize_closure_t * = NULL) const
{
TRACE_SANITIZE (this);
return_trace (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this));
}
protected:
OffsetTo<LangSys>
defaultLangSys;
RecordArrayOf<LangSys>
langSys;
public:
DEFINE_SIZE_ARRAY (4, langSys);
};
typedef RecordListOf<Script> ScriptList;
struct FeatureParamsSize
{
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
if (unlikely (!c->check_struct (this))) return_trace (false);
if (!designSize)
return_trace (false);
else if (subfamilyID == 0 &&
subfamilyNameID == 0 &&
rangeStart == 0 &&
rangeEnd == 0)
return_trace (true);
else if (designSize < rangeStart ||
designSize > rangeEnd ||
subfamilyNameID < 256 ||
subfamilyNameID > 32767)
return_trace (false);
else
return_trace (true);
}
USHORT designSize;
USHORT subfamilyID;
USHORT subfamilyNameID;
USHORT rangeStart;
USHORT rangeEnd;
public:
DEFINE_SIZE_STATIC (10);
};
struct FeatureParamsStylisticSet
{
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
}
USHORT version;
USHORT uiNameID;
public:
DEFINE_SIZE_STATIC (4);
};
struct FeatureParamsCharacterVariants
{
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
characters.sanitize (c));
}
USHORT format;
USHORT featUILableNameID;
USHORT featUITooltipTextNameID;
USHORT sampleTextNameID;
USHORT numNamedParameters;
USHORT firstParamUILabelNameID;
ArrayOf<UINT24>
characters;
public:
DEFINE_SIZE_ARRAY (14, characters);
};
struct FeatureParams
{
inline bool sanitize (hb_sanitize_context_t *c, hb_tag_t tag) const
{
TRACE_SANITIZE (this);
if (tag == HB_TAG ('s','i','z','e'))
return_trace (u.size.sanitize (c));
if ((tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0'))
return_trace (u.stylisticSet.sanitize (c));
if ((tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0'))
return_trace (u.characterVariants.sanitize (c));
return_trace (true);
}
inline const FeatureParamsSize& get_size_params (hb_tag_t tag) const
{
if (tag == HB_TAG ('s','i','z','e'))
return u.size;
return Null(FeatureParamsSize);
}
private:
union {
FeatureParamsSize size;
FeatureParamsStylisticSet stylisticSet;
FeatureParamsCharacterVariants characterVariants;
} u;
DEFINE_SIZE_STATIC (17);
};
struct Feature
{
inline unsigned int get_lookup_count (void) const
{ return lookupIndex.len; }
inline hb_tag_t get_lookup_index (unsigned int i) const
{ return lookupIndex[i]; }
inline unsigned int get_lookup_indexes (unsigned int start_index,
unsigned int *lookup_count ,
unsigned int *lookup_tags ) const
{ return lookupIndex.get_indexes (start_index, lookup_count, lookup_tags); }
inline const FeatureParams &get_feature_params (void) const
{ return this+featureParams; }
inline bool sanitize (hb_sanitize_context_t *c,
const Record<Feature>::sanitize_closure_t *closure = NULL) const
{
TRACE_SANITIZE (this);
if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c))))
return_trace (false);
OffsetTo<FeatureParams> orig_offset = featureParams;
if (unlikely (!featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE)))
return_trace (false);
if (likely (orig_offset.is_null ()))
return_trace (true);
if (featureParams == 0 && closure &&
closure->tag == HB_TAG ('s','i','z','e') &&
closure->list_base && closure->list_base < this)
{
unsigned int new_offset_int = (unsigned int) orig_offset -
(((char *) this) - ((char *) closure->list_base));
OffsetTo<FeatureParams> new_offset;
new_offset.set (new_offset_int);
if (new_offset == new_offset_int &&
c->try_set (&featureParams, new_offset) &&
!featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE))
return_trace (false);
if (c->edit_count > 1)
c->edit_count--;
}
return_trace (true);
}
OffsetTo<FeatureParams>
featureParams;
IndexArray lookupIndex;
public:
DEFINE_SIZE_ARRAY (4, lookupIndex);
};
typedef RecordListOf<Feature> FeatureList;
struct LookupFlag : USHORT
{
enum Flags {
RightToLeft = 0x0001u,
IgnoreBaseGlyphs = 0x0002u,
IgnoreLigatures = 0x0004u,
IgnoreMarks = 0x0008u,
IgnoreFlags = 0x000Eu,
UseMarkFilteringSet = 0x0010u,
Reserved = 0x00E0u,
MarkAttachmentType = 0xFF00u
};
public:
DEFINE_SIZE_STATIC (2);
};
}
HB_MARK_AS_FLAG_T (OT::LookupFlag::Flags);
namespace OT {
struct Lookup
{
inline unsigned int get_subtable_count (void) const { return subTable.len; }
template <typename SubTableType>
inline const SubTableType& get_subtable (unsigned int i) const
{ return this+CastR<OffsetArrayOf<SubTableType> > (subTable)[i]; }
template <typename SubTableType>
inline const OffsetArrayOf<SubTableType>& get_subtables (void) const
{ return CastR<OffsetArrayOf<SubTableType> > (subTable); }
template <typename SubTableType>
inline OffsetArrayOf<SubTableType>& get_subtables (void)
{ return CastR<OffsetArrayOf<SubTableType> > (subTable); }
inline unsigned int get_type (void) const { return lookupType; }
inline uint32_t get_props (void) const
{
unsigned int flag = lookupFlag;
if (unlikely (flag & LookupFlag::UseMarkFilteringSet))
{
const USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
flag += (markFilteringSet << 16);
}
return flag;
}
template <typename SubTableType, typename context_t>
inline typename context_t::return_t dispatch (context_t *c) const
{
unsigned int lookup_type = get_type ();
TRACE_DISPATCH (this, lookup_type);
unsigned int count = get_subtable_count ();
for (unsigned int i = 0; i < count; i++) {
typename context_t::return_t r = get_subtable<SubTableType> (i).dispatch (c, lookup_type);
if (c->stop_sublookup_iteration (r))
return_trace (r);
}
return_trace (c->default_return_value ());
}
inline bool serialize (hb_serialize_context_t *c,
unsigned int lookup_type,
uint32_t lookup_props,
unsigned int num_subtables)
{
TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (*this))) return_trace (false);
lookupType.set (lookup_type);
lookupFlag.set (lookup_props & 0xFFFFu);
if (unlikely (!subTable.serialize (c, num_subtables))) return_trace (false);
if (lookupFlag & LookupFlag::UseMarkFilteringSet)
{
USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
markFilteringSet.set (lookup_props >> 16);
}
return_trace (true);
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
if (!(c->check_struct (this) && subTable.sanitize (c))) return_trace (false);
if (lookupFlag & LookupFlag::UseMarkFilteringSet)
{
const USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
if (!markFilteringSet.sanitize (c)) return_trace (false);
}
return_trace (true);
}
private:
USHORT lookupType;
USHORT lookupFlag;
ArrayOf<Offset<> >
subTable;
USHORT markFilteringSetX[VAR];
public:
DEFINE_SIZE_ARRAY2 (6, subTable, markFilteringSetX);
};
typedef OffsetListOf<Lookup> LookupList;
struct CoverageFormat1
{
friend struct Coverage;
private:
inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
{
int i = glyphArray.bsearch (glyph_id);
ASSERT_STATIC (((unsigned int) -1) == NOT_COVERED);
return i;
}
inline bool serialize (hb_serialize_context_t *c,
Supplier<GlyphID> &glyphs,
unsigned int num_glyphs)
{
TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (*this))) return_trace (false);
glyphArray.len.set (num_glyphs);
if (unlikely (!c->extend (glyphArray))) return_trace (false);
for (unsigned int i = 0; i < num_glyphs; i++)
glyphArray[i] = glyphs[i];
glyphs.advance (num_glyphs);
return_trace (true);
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (glyphArray.sanitize (c));
}
inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
return glyphs->has (glyphArray[index]);
}
template <typename set_t>
inline void add_coverage (set_t *glyphs) const {
unsigned int count = glyphArray.len;
for (unsigned int i = 0; i < count; i++)
glyphs->add (glyphArray[i]);
}
public:
struct Iter {
inline void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; };
inline bool more (void) { return i < c->glyphArray.len; }
inline void next (void) { i++; }
inline hb_codepoint_t get_glyph (void) { return c->glyphArray[i]; }
inline unsigned int get_coverage (void) { return i; }
private:
const struct CoverageFormat1 *c;
unsigned int i;
};
private:
protected:
USHORT coverageFormat;
SortedArrayOf<GlyphID>
glyphArray;
public:
DEFINE_SIZE_ARRAY (4, glyphArray);
};
struct CoverageFormat2
{
friend struct Coverage;
private:
inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
{
int i = rangeRecord.bsearch (glyph_id);
if (i != -1) {
const RangeRecord &range = rangeRecord[i];
return (unsigned int) range.value + (glyph_id - range.start);
}
return NOT_COVERED;
}
inline bool serialize (hb_serialize_context_t *c,
Supplier<GlyphID> &glyphs,
unsigned int num_glyphs)
{
TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (*this))) return_trace (false);
if (unlikely (!num_glyphs))
{
rangeRecord.len.set (0);
return_trace (true);
}
unsigned int num_ranges = 1;
for (unsigned int i = 1; i < num_glyphs; i++)
if (glyphs[i - 1] + 1 != glyphs[i])
num_ranges++;
rangeRecord.len.set (num_ranges);
if (unlikely (!c->extend (rangeRecord))) return_trace (false);
unsigned int range = 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]) {
range++;
rangeRecord[range].start = glyphs[i];
rangeRecord[range].value.set (i);
rangeRecord[range].end = glyphs[i];
} else {
rangeRecord[range].end = glyphs[i];
}
glyphs.advance (num_glyphs);
return_trace (true);
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (rangeRecord.sanitize (c));
}
inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
unsigned int i;
unsigned int count = rangeRecord.len;
for (i = 0; i < count; i++) {
const RangeRecord &range = rangeRecord[i];
if (range.value <= index &&
index < (unsigned int) range.value + (range.end - range.start) &&
range.intersects (glyphs))
return true;
else if (index < range.value)
return false;
}
return false;
}
template <typename set_t>
inline void add_coverage (set_t *glyphs) const {
unsigned int count = rangeRecord.len;
for (unsigned int i = 0; i < count; i++)
rangeRecord[i].add_coverage (glyphs);
}
public:
struct Iter
{
inline void init (const CoverageFormat2 &c_)
{
c = &c_;
coverage = 0;
i = 0;
j = c->rangeRecord.len ? c_.rangeRecord[0].start : 0;
}
inline bool more (void) { return i < c->rangeRecord.len; }
inline void next (void)
{
if (j >= c->rangeRecord[i].end)
{
i++;
if (more ())
{
j = c->rangeRecord[i].start;
coverage = c->rangeRecord[i].value;
}
return;
}
coverage++;
j++;
}
inline hb_codepoint_t get_glyph (void) { return j; }
inline unsigned int get_coverage (void) { return coverage; }
private:
const struct CoverageFormat2 *c;
unsigned int i, j, coverage;
};
private:
protected:
USHORT coverageFormat;
SortedArrayOf<RangeRecord>
rangeRecord;
public:
DEFINE_SIZE_ARRAY (4, rangeRecord);
};
struct Coverage
{
inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
{
switch (u.format) {
case 1: return u.format1.get_coverage(glyph_id);
case 2: return u.format2.get_coverage(glyph_id);
default:return NOT_COVERED;
}
}
inline bool serialize (hb_serialize_context_t *c,
Supplier<GlyphID> &glyphs,
unsigned int num_glyphs)
{
TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (*this))) return_trace (false);
unsigned int num_ranges = 1;
for (unsigned int i = 1; i < num_glyphs; i++)
if (glyphs[i - 1] + 1 != glyphs[i])
num_ranges++;
u.format.set (num_glyphs * 2 < num_ranges * 3 ? 1 : 2);
switch (u.format) {
case 1: return_trace (u.format1.serialize (c, glyphs, num_glyphs));
case 2: return_trace (u.format2.serialize (c, glyphs, num_glyphs));
default:return_trace (false);
}
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
if (!u.format.sanitize (c)) return_trace (false);
switch (u.format) {
case 1: return_trace (u.format1.sanitize (c));
case 2: return_trace (u.format2.sanitize (c));
default:return_trace (true);
}
}
inline bool intersects (const hb_set_t *glyphs) const {
Coverage::Iter iter;
for (iter.init (*this); iter.more (); iter.next ()) {
if (glyphs->has (iter.get_glyph ()))
return true;
}
return false;
}
inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
switch (u.format) {
case 1: return u.format1.intersects_coverage (glyphs, index);
case 2: return u.format2.intersects_coverage (glyphs, index);
default:return false;
}
}
template <typename set_t>
inline void add_coverage (set_t *glyphs) const {
switch (u.format) {
case 1: u.format1.add_coverage (glyphs); break;
case 2: u.format2.add_coverage (glyphs); break;
default: break;
}
}
struct Iter {
Iter (void) : format (0) {};
inline void init (const Coverage &c_) {
format = c_.u.format;
switch (format) {
case 1: u.format1.init (c_.u.format1); return;
case 2: u.format2.init (c_.u.format2); return;
default: return;
}
}
inline bool more (void) {
switch (format) {
case 1: return u.format1.more ();
case 2: return u.format2.more ();
default:return false;
}
}
inline void next (void) {
switch (format) {
case 1: u.format1.next (); break;
case 2: u.format2.next (); break;
default: break;
}
}
inline hb_codepoint_t get_glyph (void) {
switch (format) {
case 1: return u.format1.get_glyph ();
case 2: return u.format2.get_glyph ();
default:return 0;
}
}
inline unsigned int get_coverage (void) {
switch (format) {
case 1: return u.format1.get_coverage ();
case 2: return u.format2.get_coverage ();
default:return -1;
}
}
private:
unsigned int format;
union {
CoverageFormat1::Iter format1;
CoverageFormat2::Iter format2;
} u;
};
protected:
union {
USHORT format;
CoverageFormat1 format1;
CoverageFormat2 format2;
} u;
public:
DEFINE_SIZE_UNION (2, format);
};
struct ClassDefFormat1
{
friend struct ClassDef;
private:
inline unsigned int get_class (hb_codepoint_t glyph_id) const
{
unsigned int i = (unsigned int) (glyph_id - startGlyph);
if (unlikely (i < classValue.len))
return classValue[i];
return 0;
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) && classValue.sanitize (c));
}
template <typename set_t>
inline void add_class (set_t *glyphs, unsigned int klass) const {
unsigned int count = classValue.len;
for (unsigned int i = 0; i < count; i++)
if (classValue[i] == klass)
glyphs->add (startGlyph + i);
}
inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
unsigned int count = classValue.len;
if (klass == 0)
{
hb_codepoint_t g = -1;
if (!hb_set_next (glyphs, &g))
return false;
if (g < startGlyph)
return true;
g = startGlyph + count - 1;
if (hb_set_next (glyphs, &g))
return true;
}
for (unsigned int i = 0; i < count; i++)
if (classValue[i] == klass && glyphs->has (startGlyph + i))
return true;
return false;
}
protected:
USHORT classFormat;
GlyphID startGlyph;
ArrayOf<USHORT>
classValue;
public:
DEFINE_SIZE_ARRAY (6, classValue);
};
struct ClassDefFormat2
{
friend struct ClassDef;
private:
inline unsigned int get_class (hb_codepoint_t glyph_id) const
{
int i = rangeRecord.bsearch (glyph_id);
if (unlikely (i != -1))
return rangeRecord[i].value;
return 0;
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (rangeRecord.sanitize (c));
}
template <typename set_t>
inline void add_class (set_t *glyphs, unsigned int klass) const {
unsigned int count = rangeRecord.len;
for (unsigned int i = 0; i < count; i++)
if (rangeRecord[i].value == klass)
rangeRecord[i].add_coverage (glyphs);
}
inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
unsigned int count = rangeRecord.len;
if (klass == 0)
{
hb_codepoint_t g = (hb_codepoint_t) -1;
for (unsigned int i = 0; i < count; i++)
{
if (!hb_set_next (glyphs, &g))
break;
if (g < rangeRecord[i].start)
return true;
g = rangeRecord[i].end;
}
if (g != (hb_codepoint_t) -1 && hb_set_next (glyphs, &g))
return true;
}
for (unsigned int i = 0; i < count; i++)
if (rangeRecord[i].value == klass && rangeRecord[i].intersects (glyphs))
return true;
return false;
}
protected:
USHORT classFormat;
SortedArrayOf<RangeRecord>
rangeRecord;
public:
DEFINE_SIZE_ARRAY (4, rangeRecord);
};
struct ClassDef
{
inline unsigned int get_class (hb_codepoint_t glyph_id) const
{
switch (u.format) {
case 1: return u.format1.get_class(glyph_id);
case 2: return u.format2.get_class(glyph_id);
default:return 0;
}
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
if (!u.format.sanitize (c)) return_trace (false);
switch (u.format) {
case 1: return_trace (u.format1.sanitize (c));
case 2: return_trace (u.format2.sanitize (c));
default:return_trace (true);
}
}
inline void add_class (hb_set_t *glyphs, unsigned int klass) const {
switch (u.format) {
case 1: u.format1.add_class (glyphs, klass); return;
case 2: u.format2.add_class (glyphs, klass); return;
default:return;
}
}
inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
switch (u.format) {
case 1: return u.format1.intersects_class (glyphs, klass);
case 2: return u.format2.intersects_class (glyphs, klass);
default:return false;
}
}
protected:
union {
USHORT format;
ClassDefFormat1 format1;
ClassDefFormat2 format2;
} u;
public:
DEFINE_SIZE_UNION (2, format);
};
struct VarRegionAxis
{
inline float evaluate (int coord) const
{
int start = startCoord, peak = peakCoord, end = endCoord;
if (unlikely (start > peak || peak > end))
return 1.;
if (unlikely (start < 0 && end > 0 && peak != 0))
return 1.;
if (peak == 0 || coord == peak)
return 1.;
if (coord <= start || end <= coord)
return 0.;
if (coord < peak)
return float (coord - start) / (peak - start);
else
return float (end - coord) / (end - peak);
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
}
public:
F2DOT14 startCoord;
F2DOT14 peakCoord;
F2DOT14 endCoord;
public:
DEFINE_SIZE_STATIC (6);
};
struct VarRegionList
{
inline float evaluate (unsigned int region_index,
int *coords, unsigned int coord_len) const
{
if (unlikely (region_index >= regionCount))
return 0.;
const VarRegionAxis *axes = axesZ + (region_index * axisCount);
float v = 1.;
unsigned int count = MIN (coord_len, (unsigned int) axisCount);
for (unsigned int i = 0; i < count; i++)
{
float factor = axes[i].evaluate (coords[i]);
if (factor == 0.)
return 0.;
v *= factor;
}
return v;
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
c->check_array (axesZ, axesZ[0].static_size,
(unsigned int) axisCount * (unsigned int) regionCount));
}
protected:
USHORT axisCount;
USHORT regionCount;
VarRegionAxis axesZ[VAR];
public:
DEFINE_SIZE_ARRAY (4, axesZ);
};
struct VarData
{
inline unsigned int get_row_size (void) const
{ return shortCount + regionIndices.len; }
inline unsigned int get_size (void) const
{ return itemCount * get_row_size (); }
inline float get_delta (unsigned int inner,
int *coords, unsigned int coord_count,
const VarRegionList ®ions) const
{
if (unlikely (inner >= itemCount))
return 0.;
unsigned int count = regionIndices.len;
unsigned int scount = shortCount;
const BYTE *bytes = &StructAfter<BYTE> (regionIndices);
const BYTE *row = bytes + inner * (scount + count);
float delta = 0.;
unsigned int i = 0;
const SHORT *scursor = reinterpret_cast<const SHORT *> (row);
for (; i < scount; i++)
{
float scalar = regions.evaluate (regionIndices.array[i], coords, coord_count);
delta += scalar * *scursor++;
}
const INT8 *bcursor = reinterpret_cast<const INT8 *> (scursor);
for (; i < count; i++)
{
float scalar = regions.evaluate (regionIndices.array[i], coords, coord_count);
delta += scalar * *bcursor++;
}
return delta;
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
regionIndices.sanitize(c) &&
shortCount <= regionIndices.len &&
c->check_array (&StructAfter<BYTE> (regionIndices),
get_row_size (), itemCount));
}
protected:
USHORT itemCount;
USHORT shortCount;
ArrayOf<USHORT> regionIndices;
BYTE bytesX[VAR];
public:
DEFINE_SIZE_ARRAY2 (6, regionIndices, bytesX);
};
struct VariationStore
{
inline float get_delta (unsigned int outer, unsigned int inner,
int *coords, unsigned int coord_count) const
{
if (unlikely (outer >= dataSets.len))
return 0.;
return (this+dataSets[outer]).get_delta (inner,
coords, coord_count,
this+regions);
}
inline float get_delta (unsigned int index,
int *coords, unsigned int coord_count) const
{
unsigned int outer = index >> 16;
unsigned int inner = index & 0xFFFF;
return get_delta (outer, inner, coords, coord_count);
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
format == 1 &&
regions.sanitize (c, this) &&
dataSets.sanitize (c, this));
}
protected:
USHORT format;
LOffsetTo<VarRegionList> regions;
OffsetArrayOf<VarData, ULONG> dataSets;
public:
DEFINE_SIZE_ARRAY (8, dataSets);
};
struct ConditionFormat1
{
friend struct Condition;
private:
inline bool evaluate (const int *coords, unsigned int coord_len) const
{
int coord = axisIndex < coord_len ? coords[axisIndex] : 0;
return filterRangeMinValue <= coord && coord <= filterRangeMaxValue;
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
}
protected:
USHORT format;
USHORT axisIndex;
F2DOT14 filterRangeMinValue;
F2DOT14 filterRangeMaxValue;
public:
DEFINE_SIZE_STATIC (8);
};
struct Condition
{
inline bool evaluate (const int *coords, unsigned int coord_len) const
{
switch (u.format) {
case 1: return u.format1.evaluate (coords, coord_len);
default:return false;
}
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
if (!u.format.sanitize (c)) return_trace (false);
switch (u.format) {
case 1: return_trace (u.format1.sanitize (c));
default:return_trace (true);
}
}
protected:
union {
USHORT format;
ConditionFormat1 format1;
} u;
public:
DEFINE_SIZE_UNION (2, format);
};
struct ConditionSet
{
inline bool evaluate (const int *coords, unsigned int coord_len) const
{
unsigned int count = conditions.len;
for (unsigned int i = 0; i < count; i++)
if (!(this+conditions.array[i]).evaluate (coords, coord_len))
return false;
return true;
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (conditions.sanitize (c, this));
}
protected:
OffsetArrayOf<Condition, ULONG> conditions;
public:
DEFINE_SIZE_ARRAY (2, conditions);
};
struct FeatureTableSubstitutionRecord
{
friend struct FeatureTableSubstitution;
inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) && feature.sanitize (c, base));
}
protected:
USHORT featureIndex;
LOffsetTo<Feature> feature;
public:
DEFINE_SIZE_STATIC (6);
};
struct FeatureTableSubstitution
{
inline const Feature *find_substitute (unsigned int feature_index) const
{
unsigned int count = substitutions.len;
for (unsigned int i = 0; i < count; i++)
{
const FeatureTableSubstitutionRecord &record = substitutions.array[i];
if (record.featureIndex == feature_index)
return &(this+record.feature);
}
return NULL;
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (version.sanitize (c) &&
likely (version.major == 1) &&
substitutions.sanitize (c, this));
}
protected:
FixedVersion<> version;
ArrayOf<FeatureTableSubstitutionRecord>
substitutions;
public:
DEFINE_SIZE_ARRAY (6, substitutions);
};
struct FeatureVariationRecord
{
friend struct FeatureVariations;
inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
return_trace (conditions.sanitize (c, base) &&
substitutions.sanitize (c, base));
}
protected:
LOffsetTo<ConditionSet>
conditions;
LOffsetTo<FeatureTableSubstitution>
substitutions;
public:
DEFINE_SIZE_STATIC (8);
};
struct FeatureVariations
{
static const unsigned int NOT_FOUND_INDEX = 0xFFFFFFFFu;
inline bool find_index (const int *coords, unsigned int coord_len,
unsigned int *index) const
{
unsigned int count = varRecords.len;
for (unsigned int i = 0; i < count; i++)
{
const FeatureVariationRecord &record = varRecords.array[i];
if ((this+record.conditions).evaluate (coords, coord_len))
{
*index = i;
return true;
}
}
*index = NOT_FOUND_INDEX;
return false;
}
inline const Feature *find_substitute (unsigned int variations_index,
unsigned int feature_index) const
{
const FeatureVariationRecord &record = varRecords[variations_index];
return (this+record.substitutions).find_substitute (feature_index);
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (version.sanitize (c) &&
likely (version.major == 1) &&
varRecords.sanitize (c, this));
}
protected:
FixedVersion<> version;
LArrayOf<FeatureVariationRecord>
varRecords;
public:
DEFINE_SIZE_ARRAY (8, varRecords);
};
struct HintingDevice
{
friend struct Device;
private:
inline hb_position_t get_x_delta (hb_font_t *font) const
{ return get_delta (font->x_ppem, font->x_scale); }
inline hb_position_t get_y_delta (hb_font_t *font) const
{ return get_delta (font->y_ppem, font->y_scale); }
inline unsigned int get_size (void) const
{
unsigned int f = deltaFormat;
if (unlikely (f < 1 || f > 3 || startSize > endSize)) return 3 * USHORT::static_size;
return USHORT::static_size * (4 + ((endSize - startSize) >> (4 - f)));
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) && c->check_range (this, this->get_size ()));
}
private:
inline int get_delta (unsigned int ppem, int scale) const
{
if (!ppem) return 0;
int pixels = get_delta_pixels (ppem);
if (!pixels) return 0;
return (int) (pixels * (int64_t) scale / ppem);
}
inline int get_delta_pixels (unsigned int ppem_size) const
{
unsigned int f = deltaFormat;
if (unlikely (f < 1 || f > 3))
return 0;
if (ppem_size < startSize || ppem_size > endSize)
return 0;
unsigned int s = ppem_size - startSize;
unsigned int byte = deltaValue[s >> (4 - f)];
unsigned int bits = (byte >> (16 - (((s & ((1 << (4 - f)) - 1)) + 1) << f)));
unsigned int mask = (0xFFFFu >> (16 - (1 << f)));
int delta = bits & mask;
if ((unsigned int) delta >= ((mask + 1) >> 1))
delta -= mask + 1;
return delta;
}
protected:
USHORT startSize;
USHORT endSize;
USHORT deltaFormat;
USHORT deltaValue[VAR];
public:
DEFINE_SIZE_ARRAY (6, deltaValue);
};
struct VariationDevice
{
friend struct Device;
private:
inline hb_position_t get_x_delta (hb_font_t *font, const VariationStore &store) const
{ return font->em_scalef_x (get_delta (font, store)); }
inline hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store) const
{ return font->em_scalef_y (get_delta (font, store)); }
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
}
private:
inline float get_delta (hb_font_t *font, const VariationStore &store) const
{
return store.get_delta (outerIndex, innerIndex, font->coords, font->num_coords);
}
protected:
USHORT outerIndex;
USHORT innerIndex;
USHORT deltaFormat;
public:
DEFINE_SIZE_STATIC (6);
};
struct DeviceHeader
{
protected:
USHORT reserved1;
USHORT reserved2;
public:
USHORT format;
public:
DEFINE_SIZE_STATIC (6);
};
struct Device
{
inline hb_position_t get_x_delta (hb_font_t *font, const VariationStore &store=Null(VariationStore)) const
{
switch (u.b.format)
{
case 1: case 2: case 3:
return u.hinting.get_x_delta (font);
case 0x8000:
return u.variation.get_x_delta (font, store);
default:
return 0;
}
}
inline hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store=Null(VariationStore)) const
{
switch (u.b.format)
{
case 1: case 2: case 3:
return u.hinting.get_y_delta (font);
case 0x8000:
return u.variation.get_y_delta (font, store);
default:
return 0;
}
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
if (!u.b.format.sanitize (c)) return_trace (false);
switch (u.b.format) {
case 1: case 2: case 3:
return_trace (u.hinting.sanitize (c));
case 0x8000:
return_trace (u.variation.sanitize (c));
default:
return_trace (true);
}
}
protected:
union {
DeviceHeader b;
HintingDevice hinting;
VariationDevice variation;
} u;
public:
DEFINE_SIZE_UNION (6, b);
};
}
#endif