Merge branch 'master' into cff-subset

This commit is contained in:
Michiharu Ariza 2018-10-11 10:53:48 -07:00
commit ce6639cd27
13 changed files with 479 additions and 128 deletions

View File

@ -45,32 +45,47 @@ struct Anchor
return_trace (c->check_struct (this)); return_trace (c->check_struct (this));
} }
public:
FWORD xCoordinate; FWORD xCoordinate;
FWORD yCoordinate; FWORD yCoordinate;
public: public:
DEFINE_SIZE_STATIC (4); DEFINE_SIZE_STATIC (4);
}; };
typedef LArrayOf<Anchor> GlyphAnchors;
struct ankr struct ankr
{ {
static const hb_tag_t tableTag = HB_AAT_TAG_ankr; static const hb_tag_t tableTag = HB_AAT_TAG_ankr;
inline const Anchor &get_anchor (hb_codepoint_t glyph_id,
unsigned int i,
unsigned int num_glyphs,
const char *end) const
{
unsigned int offset = (this+lookupTable).get_value_or_null (glyph_id, num_glyphs);
const GlyphAnchors &anchors = StructAtOffset<GlyphAnchors> (&(this+anchorData), offset);
/* TODO Use sanitizer; to avoid overflows and more. */
if (unlikely ((const char *) &anchors + anchors.get_size () > end))
return Null(Anchor);
return anchors[i];
}
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) && return_trace (likely (c->check_struct (this) &&
version == 0 && version == 0 &&
lookupTable.sanitize (c, this) && lookupTable.sanitize (c, this)));
anchors.sanitize (c, this)));
} }
protected: protected:
HBUINT16 version; /* Version number (set to zero) */ HBUINT16 version; /* Version number (set to zero) */
HBUINT16 flags; /* Flags (currently unused; set to zero) */ HBUINT16 flags; /* Flags (currently unused; set to zero) */
LOffsetTo<Lookup<HBUINT16> > LOffsetTo<Lookup<Offset<HBUINT16, false> > >
lookupTable; /* Offset to the table's lookup table */ lookupTable; /* Offset to the table's lookup table */
LOffsetTo<LArrayOf<Anchor> > LOffsetTo<HBUINT8>
anchors; /* Offset to the glyph data table */ anchorData; /* Offset to the glyph data table */
public: public:
DEFINE_SIZE_STATIC (12); DEFINE_SIZE_STATIC (12);

View File

@ -514,6 +514,7 @@ struct StateTableDriver
}; };
struct ankr;
struct hb_aat_apply_context_t : struct hb_aat_apply_context_t :
hb_dispatch_context_t<hb_aat_apply_context_t, bool, HB_DEBUG_APPLY> hb_dispatch_context_t<hb_aat_apply_context_t, bool, HB_DEBUG_APPLY>
@ -529,6 +530,8 @@ struct hb_aat_apply_context_t :
hb_face_t *face; hb_face_t *face;
hb_buffer_t *buffer; hb_buffer_t *buffer;
hb_sanitize_context_t sanitizer; hb_sanitize_context_t sanitizer;
const ankr &ankr_table;
const char *ankr_end;
/* Unused. For debug tracing only. */ /* Unused. For debug tracing only. */
unsigned int lookup_index; unsigned int lookup_index;
@ -537,11 +540,15 @@ struct hb_aat_apply_context_t :
inline hb_aat_apply_context_t (hb_ot_shape_plan_t *plan_, inline hb_aat_apply_context_t (hb_ot_shape_plan_t *plan_,
hb_font_t *font_, hb_font_t *font_,
hb_buffer_t *buffer_, hb_buffer_t *buffer_,
hb_blob_t *table) : hb_blob_t *blob = const_cast<hb_blob_t *> (&Null(hb_blob_t)),
const ankr &ankr_table_ = Null(ankr),
const char *ankr_end_ = nullptr) :
plan (plan_), font (font_), face (font->face), buffer (buffer_), plan (plan_), font (font_), face (font->face), buffer (buffer_),
sanitizer (), lookup_index (0), debug_depth (0) sanitizer (),
ankr_table (ankr_table_), ankr_end (ankr_end_),
lookup_index (0), debug_depth (0)
{ {
sanitizer.init (table); sanitizer.init (blob);
sanitizer.set_num_glyphs (face->get_num_glyphs ()); sanitizer.set_num_glyphs (face->get_num_glyphs ());
sanitizer.start_processing (); sanitizer.start_processing ();
sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX); sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX);

View File

@ -30,6 +30,7 @@
#include "hb-open-type.hh" #include "hb-open-type.hh"
#include "hb-aat-layout-common.hh" #include "hb-aat-layout-common.hh"
#include "hb-ot-layout-gpos-table.hh"
#include "hb-ot-kern-table.hh" #include "hb-ot-kern-table.hh"
/* /*
@ -99,6 +100,100 @@ struct KerxSubTableFormat0
struct KerxSubTableFormat1 struct KerxSubTableFormat1
{ {
struct EntryData
{
HBUINT16 kernActionIndex;/* Index into the kerning value array. If
* this index is 0xFFFF, then no kerning
* is to be performed. */
public:
DEFINE_SIZE_STATIC (2);
};
struct driver_context_t
{
static const bool in_place = true;
enum Flags
{
Push = 0x8000, /* If set, push this glyph on the kerning stack. */
DontAdvance = 0x4000, /* If set, don't advance to the next glyph
* before going to the new state. */
Reset = 0x2000, /* If set, reset the kerning data (clear the stack) */
Reserved = 0x1FFF, /* Not used; set to 0. */
};
inline driver_context_t (const KerxSubTableFormat1 *table,
hb_aat_apply_context_t *c_) :
c (c_),
/* Apparently the offset kernAction is from the beginning of the state-machine,
* similar to offsets in morx table, NOT from beginning of this table, like
* other subtables in kerx. Discovered via testing. */
kernAction (&table->machine + table->kernAction),
depth (0) {}
inline bool is_actionable (StateTableDriver<EntryData> *driver,
const Entry<EntryData> *entry)
{
return entry->data.kernActionIndex != 0xFFFF;
}
inline bool transition (StateTableDriver<EntryData> *driver,
const Entry<EntryData> *entry)
{
hb_buffer_t *buffer = driver->buffer;
unsigned int flags = entry->flags;
if (flags & Reset)
{
depth = 0;
}
if (flags & Push)
{
if (likely (depth < ARRAY_LENGTH (stack)))
stack[depth++] = buffer->idx;
else
depth = 0; /* Probably not what CoreText does, but better? */
}
if (entry->data.kernActionIndex != 0xFFFF)
{
const FWORD *actions = &kernAction[entry->data.kernActionIndex];
if (!c->sanitizer.check_array (actions, depth))
{
depth = 0;
return false;
}
hb_mask_t kern_mask = c->plan->kern_mask;
for (unsigned int i = 0; i < depth; i++)
{
/* Apparently, when spec says "Each pops one glyph from the kerning stack
* and applies the kerning value to it.", it doesn't mean it in that order.
* The deepest item in the stack corresponds to the first item in the action
* list. Discovered by testing. */
unsigned int idx = stack[i];
int v = *actions++;
if (buffer->info[idx].mask & kern_mask)
{
/* XXX Non-forward direction... */
if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
buffer->pos[idx].x_advance += c->font->em_scale_x (v);
else
buffer->pos[idx].y_advance += c->font->em_scale_y (v);
}
}
depth = 0;
}
return true;
}
private:
hb_aat_apply_context_t *c;
const UnsizedArrayOf<FWORD> &kernAction;
unsigned int stack[8];
unsigned int depth;
};
inline bool apply (hb_aat_apply_context_t *c) const inline bool apply (hb_aat_apply_context_t *c) const
{ {
TRACE_APPLY (this); TRACE_APPLY (this);
@ -106,7 +201,10 @@ struct KerxSubTableFormat1
if (!c->plan->requested_kerning) if (!c->plan->requested_kerning)
return false; return false;
/* TODO */ driver_context_t dc (this, c);
StateTableDriver<EntryData> driver (machine, c->buffer, c->font->face);
driver.drive (&dc);
return_trace (true); return_trace (true);
} }
@ -114,14 +212,13 @@ struct KerxSubTableFormat1
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) && return_trace (likely (machine.sanitize (c)));
stateHeader.sanitize (c)));
} }
protected: protected:
KerxSubTableHeader header; KerxSubTableHeader header;
StateTable<HBUINT16> stateHeader; StateTable<EntryData> machine;
LOffsetTo<ArrayOf<HBUINT16> > valueTable; LOffsetTo<UnsizedArrayOf<FWORD>, false> kernAction;
public: public:
DEFINE_SIZE_STATIC (32); DEFINE_SIZE_STATIC (32);
}; };
@ -136,7 +233,7 @@ struct KerxSubTableFormat2
unsigned int offset = l + r; unsigned int offset = l + r;
const FWORD *v = &StructAtOffset<FWORD> (&(this+array), offset); const FWORD *v = &StructAtOffset<FWORD> (&(this+array), offset);
if (unlikely ((const char *) v < (const char *) &array || if (unlikely ((const char *) v < (const char *) &array ||
(const char *) v + v->static_size - (const char *) this <= header.length)) (const char *) v + v->static_size - (const char *) this > header.length))
return 0; return 0;
return *v; return *v;
} }
@ -159,11 +256,9 @@ struct KerxSubTableFormat2
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) && return_trace (likely (rowWidth.sanitize (c) &&
rowWidth.sanitize (c) &&
leftClassTable.sanitize (c, this) && leftClassTable.sanitize (c, this) &&
rightClassTable.sanitize (c, this) && rightClassTable.sanitize (c, this)));
array.sanitize (c, this)));
} }
struct accelerator_t struct accelerator_t
@ -190,7 +285,8 @@ struct KerxSubTableFormat2
LOffsetTo<Lookup<HBUINT16> > LOffsetTo<Lookup<HBUINT16> >
rightClassTable;/* Offset from beginning of this subtable to rightClassTable;/* Offset from beginning of this subtable to
* right-hand class table. */ * right-hand class table. */
LOffsetTo<FWORD> array; /* Offset from beginning of this subtable to LOffsetTo<UnsizedArrayOf<FWORD>, false>
array; /* Offset from beginning of this subtable to
* the start of the kerning array. */ * the start of the kerning array. */
public: public:
DEFINE_SIZE_STATIC (28); DEFINE_SIZE_STATIC (28);
@ -198,11 +294,152 @@ struct KerxSubTableFormat2
struct KerxSubTableFormat4 struct KerxSubTableFormat4
{ {
struct EntryData
{
HBUINT16 ankrActionIndex;/* Either 0xFFFF (for no action) or the index of
* the action to perform. */
public:
DEFINE_SIZE_STATIC (2);
};
struct driver_context_t
{
static const bool in_place = true;
enum Flags
{
Mark = 0x8000, /* If set, remember this glyph as the marked glyph. */
DontAdvance = 0x4000, /* If set, don't advance to the next glyph before
* going to the new state. */
Reserved = 0x3FFF, /* Not used; set to 0. */
};
enum SubTableFlags
{
ActionType = 0xC0000000, /* A two-bit field containing the action type. */
Unused = 0x3F000000, /* Unused - must be zero. */
Offset = 0x00FFFFFF, /* Masks the offset in bytes from the beginning
* of the subtable to the beginning of the control
* point table. */
};
inline driver_context_t (const KerxSubTableFormat4 *table,
hb_aat_apply_context_t *c_) :
c (c_),
action_type ((table->flags & ActionType) >> 30),
ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))),
mark_set (false),
mark (0) {}
inline bool is_actionable (StateTableDriver<EntryData> *driver,
const Entry<EntryData> *entry)
{
return entry->data.ankrActionIndex != 0xFFFF;
}
inline bool transition (StateTableDriver<EntryData> *driver,
const Entry<EntryData> *entry)
{
hb_buffer_t *buffer = driver->buffer;
unsigned int flags = entry->flags;
if (mark_set && entry->data.ankrActionIndex != 0xFFFF)
{
hb_glyph_position_t &o = buffer->cur_pos();
switch (action_type)
{
case 0: /* Control Point Actions.*/
{
/* indexed into glyph outline. */
const HBUINT16 *data = &ankrData[entry->data.ankrActionIndex];
if (!c->sanitizer.check_array (data, 2))
return false;
HB_UNUSED unsigned int markControlPoint = *data++;
HB_UNUSED unsigned int currControlPoint = *data++;
hb_position_t markX = 0;
hb_position_t markY = 0;
hb_position_t currX = 0;
hb_position_t currY = 0;
if (!c->font->get_glyph_contour_point_for_origin (c->buffer->info[mark].codepoint,
markControlPoint,
HB_DIRECTION_LTR /*XXX*/,
&markX, &markY) ||
!c->font->get_glyph_contour_point_for_origin (c->buffer->cur ().codepoint,
currControlPoint,
HB_DIRECTION_LTR /*XXX*/,
&currX, &currY))
return true; /* True, such that the machine continues. */
o.x_offset = markX - currX;
o.y_offset = markY - currY;
}
break;
case 1: /* Anchor Point Actions. */
{
/* Indexed into 'ankr' table. */
const HBUINT16 *data = &ankrData[entry->data.ankrActionIndex];
if (!c->sanitizer.check_array (data, 2))
return false;
unsigned int markAnchorPoint = *data++;
unsigned int currAnchorPoint = *data++;
const Anchor markAnchor = c->ankr_table.get_anchor (c->buffer->info[mark].codepoint,
markAnchorPoint,
c->face->get_num_glyphs (),
c->ankr_end);
const Anchor currAnchor = c->ankr_table.get_anchor (c->buffer->cur ().codepoint,
currAnchorPoint,
c->face->get_num_glyphs (),
c->ankr_end);
o.x_offset = c->font->em_scale_x (markAnchor.xCoordinate) - c->font->em_scale_x (currAnchor.xCoordinate);
o.y_offset = c->font->em_scale_y (markAnchor.yCoordinate) - c->font->em_scale_y (currAnchor.yCoordinate);
}
break;
case 2: /* Control Point Coordinate Actions. */
{
const FWORD *data = (const FWORD *) &ankrData[entry->data.ankrActionIndex];
if (!c->sanitizer.check_array (data, 4))
return false;
int markX = *data++;
int markY = *data++;
int currX = *data++;
int currY = *data++;
o.x_offset = c->font->em_scale_x (markX) - c->font->em_scale_x (currX);
o.y_offset = c->font->em_scale_y (markY) - c->font->em_scale_y (currY);
}
break;
}
o.attach_type() = ATTACH_TYPE_MARK;
o.attach_chain() = (int) mark - (int) buffer->idx;
buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
}
if (flags & Mark)
{
mark_set = true;
mark = buffer->idx;
}
return true;
}
private:
hb_aat_apply_context_t *c;
unsigned int action_type;
const HBUINT16 *ankrData;
bool mark_set;
unsigned int mark;
};
inline bool apply (hb_aat_apply_context_t *c) const inline bool apply (hb_aat_apply_context_t *c) const
{ {
TRACE_APPLY (this); TRACE_APPLY (this);
/* TODO */ driver_context_t dc (this, c);
StateTableDriver<EntryData> driver (machine, c->buffer, c->font->face);
driver.drive (&dc);
return_trace (true); return_trace (true);
} }
@ -211,14 +448,18 @@ struct KerxSubTableFormat4
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
/* TODO */ /* The rest of array sanitizations are done at run-time. */
return_trace (likely (c->check_struct (this))); return_trace (c->check_struct (this) &&
machine.sanitize (c) &&
flags.sanitize (c));
} }
protected: protected:
KerxSubTableHeader header; KerxSubTableHeader header;
StateTable<EntryData> machine;
HBUINT32 flags;
public: public:
DEFINE_SIZE_STATIC (12); DEFINE_SIZE_STATIC (32);
}; };
struct KerxSubTableFormat6 struct KerxSubTableFormat6
@ -241,7 +482,7 @@ struct KerxSubTableFormat6
unsigned int offset = l + r; unsigned int offset = l + r;
const FWORD32 *v = &StructAtOffset<FWORD32> (&(this+t.array), offset * sizeof (FWORD32)); const FWORD32 *v = &StructAtOffset<FWORD32> (&(this+t.array), offset * sizeof (FWORD32));
if (unlikely ((const char *) v < (const char *) &t.array || if (unlikely ((const char *) v < (const char *) &t.array ||
(const char *) v + v->static_size - (const char *) this <= header.length)) (const char *) v + v->static_size - (const char *) this > header.length))
return 0; return 0;
return *v; return *v;
} }
@ -253,7 +494,7 @@ struct KerxSubTableFormat6
unsigned int offset = l + r; unsigned int offset = l + r;
const FWORD *v = &StructAtOffset<FWORD> (&(this+t.array), offset * sizeof (FWORD)); const FWORD *v = &StructAtOffset<FWORD> (&(this+t.array), offset * sizeof (FWORD));
if (unlikely ((const char *) v < (const char *) &t.array || if (unlikely ((const char *) v < (const char *) &t.array ||
(const char *) v + v->static_size - (const char *) this <= header.length)) (const char *) v + v->static_size - (const char *) this > header.length))
return 0; return 0;
return *v; return *v;
} }
@ -281,12 +522,10 @@ struct KerxSubTableFormat6
is_long () ? is_long () ?
( (
u.l.rowIndexTable.sanitize (c, this) && u.l.rowIndexTable.sanitize (c, this) &&
u.l.columnIndexTable.sanitize (c, this) && u.l.columnIndexTable.sanitize (c, this)
u.l.array.sanitize (c, this)
) : ( ) : (
u.s.rowIndexTable.sanitize (c, this) && u.s.rowIndexTable.sanitize (c, this) &&
u.s.columnIndexTable.sanitize (c, this) && u.s.columnIndexTable.sanitize (c, this)
u.s.array.sanitize (c, this)
))); )));
} }
@ -316,13 +555,15 @@ struct KerxSubTableFormat6
{ {
LOffsetTo<Lookup<HBUINT32> > rowIndexTable; LOffsetTo<Lookup<HBUINT32> > rowIndexTable;
LOffsetTo<Lookup<HBUINT32> > columnIndexTable; LOffsetTo<Lookup<HBUINT32> > columnIndexTable;
LOffsetTo<FWORD32> array; LOffsetTo<UnsizedArrayOf<FWORD32>, false>
array;
} l; } l;
struct Short struct Short
{ {
LOffsetTo<Lookup<HBUINT16> > rowIndexTable; LOffsetTo<Lookup<HBUINT16> > rowIndexTable;
LOffsetTo<Lookup<HBUINT16> > columnIndexTable; LOffsetTo<Lookup<HBUINT16> > columnIndexTable;
LOffsetTo<FWORD> array; LOffsetTo<UnsizedArrayOf<FWORD>, false>
array;
} s; } s;
} u; } u;
public: public:

View File

@ -164,7 +164,7 @@ struct RearrangementSubtable
driver_context_t dc (this); driver_context_t dc (this);
StateTableDriver<void> driver (machine, c->buffer, c->face); StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
driver.drive (&dc); driver.drive (&dc);
return_trace (dc.ret); return_trace (dc.ret);
@ -365,7 +365,7 @@ struct LigatureSubtable
inline bool is_actionable (StateTableDriver<EntryData> *driver, inline bool is_actionable (StateTableDriver<EntryData> *driver,
const Entry<EntryData> *entry) const Entry<EntryData> *entry)
{ {
return !!(entry->flags & PerformAction); return entry->flags & PerformAction;
} }
inline bool transition (StateTableDriver<EntryData> *driver, inline bool transition (StateTableDriver<EntryData> *driver,
const Entry<EntryData> *entry) const Entry<EntryData> *entry)

View File

@ -46,28 +46,32 @@ struct TrackTableEntry
{ {
friend struct TrackData; friend struct TrackData;
inline bool sanitize (hb_sanitize_context_t *c, const void *base,
unsigned int size) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
(valuesZ.sanitize (c, base, size))));
}
private:
inline float get_track_value () const inline float get_track_value () const
{ {
return track.to_float (); return track.to_float ();
} }
inline int get_value (const void *base, unsigned int index) const inline int get_value (const void *base,
unsigned int index,
unsigned int nSizes) const
{ {
return (base+valuesZ)[index]; return hb_array_t<FWORD> ((base+valuesZ).arrayZ, nSizes)[index];
}
public:
inline bool sanitize (hb_sanitize_context_t *c, const void *base,
unsigned int nSizes) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
(valuesZ.sanitize (c, base, nSizes))));
} }
protected: protected:
Fixed track; /* Track value for this record. */ Fixed track; /* Track value for this record. */
NameID trackNameID; /* The 'name' table index for this track */ NameID trackNameID; /* The 'name' table index for this track.
* (a short word or phrase like "loose"
* or "very tight") */
OffsetTo<UnsizedArrayOf<FWORD>, HBUINT16, false> OffsetTo<UnsizedArrayOf<FWORD>, HBUINT16, false>
valuesZ; /* Offset from start of tracking table to valuesZ; /* Offset from start of tracking table to
* per-size tracking values for this track. */ * per-size tracking values for this track. */
@ -78,12 +82,20 @@ struct TrackTableEntry
struct TrackData struct TrackData
{ {
inline bool sanitize (hb_sanitize_context_t *c, const void *base) const inline float interpolate_at (unsigned int idx,
float target_size,
const TrackTableEntry &trackTableEntry,
const void *base) const
{ {
TRACE_SANITIZE (this); unsigned int sizes = nSizes;
return_trace (c->check_struct (this) && hb_array_t<Fixed> size_table ((base+sizeTable).arrayZ, sizes);
sizeTable.sanitize (c, base, nSizes) &&
trackTable.sanitize (c, nTracks, base, nSizes)); float s0 = size_table[idx].to_float ();
float s1 = size_table[idx + 1].to_float ();
float t = unlikely (s0 == s1) ? 0.f : (target_size - s0) / (s1 - s0);
return (float) t * trackTableEntry.get_value (base, idx + 1, sizes) +
((float) 1.0 - t) * trackTableEntry.get_value (base, idx, sizes);
return 0;
} }
inline float get_tracking (const void *base, float ptem) const inline float get_tracking (const void *base, float ptem) const
@ -94,48 +106,59 @@ struct TrackData
* https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html * https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html
*/ */
float csspx = ptem * 96.f / 72.f; float csspx = ptem * 96.f / 72.f;
Fixed fixed_size;
fixed_size.set_float (csspx);
/* XXX Clean this up. Make it work with nSizes==1 and 0. */
unsigned int sizes = nSizes;
/*
* Choose track.
*/
const TrackTableEntry *trackTableEntry = nullptr; const TrackTableEntry *trackTableEntry = nullptr;
for (unsigned int i = 0; i < sizes; ++i) unsigned int count = nTracks;
// For now we only seek for track entries with zero tracking value for (unsigned int i = 0; i < count; i++)
if (trackTable[i].get_track_value () == 0.f) {
trackTableEntry = &trackTable[0]; /* Note: Seems like the track entries are sorted by values. But the
* spec doesn't explicitly say that. It just mentions it in the example. */
// We couldn't match any, exit /* For now we only seek for track entries with zero tracking value */
if (trackTable[i].get_track_value () == 0.f)
{
trackTableEntry = &trackTable[0];
break;
}
}
if (!trackTableEntry) return 0.; if (!trackTableEntry) return 0.;
/*
* Choose size.
*/
unsigned int sizes = nSizes;
if (!sizes) return 0.;
if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
/* TODO bfind() */ /* TODO bfind() */
hb_array_t<Fixed> size_table ((base+sizeTable).arrayZ, sizes);
unsigned int size_index; unsigned int size_index;
UnsizedArrayOf<Fixed> size_table = base+sizeTable; for (size_index = 0; size_index < sizes; size_index++)
for (size_index = 0; size_index < sizes; ++size_index) if (size_table[size_index].to_float () >= csspx)
if (size_table[size_index] >= fixed_size)
break; break;
// TODO(ebraminio): We don't attempt to extrapolate to larger or return interpolate_at (size_index ? size_index - 1 : 0, csspx,
// smaller values for now but we should do, per spec *trackTableEntry, base);
if (size_index == sizes) }
return trackTableEntry->get_value (base, sizes - 1);
if (size_index == 0 || size_table[size_index] == fixed_size)
return trackTableEntry->get_value (base, size_index);
float s0 = size_table[size_index - 1].to_float (); inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
float s1 = size_table[size_index].to_float (); {
float t = (csspx - s0) / (s1 - s0); TRACE_SANITIZE (this);
return (float) t * trackTableEntry->get_value (base, size_index) + return_trace (c->check_struct (this) &&
((float) 1.0 - t) * trackTableEntry->get_value (base, size_index - 1); sizeTable.sanitize (c, base, nSizes) &&
trackTable.sanitize (c, nTracks, base, nSizes));
} }
protected: protected:
HBUINT16 nTracks; /* Number of separate tracks included in this table. */ HBUINT16 nTracks; /* Number of separate tracks included in this table. */
HBUINT16 nSizes; /* Number of point sizes included in this table. */ HBUINT16 nSizes; /* Number of point sizes included in this table. */
LOffsetTo<UnsizedArrayOf<Fixed>, false> LOffsetTo<UnsizedArrayOf<Fixed>, false>
sizeTable; /* Offset to array[nSizes] of size values. */ sizeTable; /* Offset from start of the tracking table to
* Array[nSizes] of size values.. */
UnsizedArrayOf<TrackTableEntry> UnsizedArrayOf<TrackTableEntry>
trackTable; /* Array[nTracks] of TrackTableEntry records. */ trackTable; /* Array[nTracks] of TrackTableEntry records. */
@ -147,6 +170,8 @@ struct trak
{ {
static const hb_tag_t tableTag = HB_AAT_TAG_trak; static const hb_tag_t tableTag = HB_AAT_TAG_trak;
inline bool has_data (void) const { return version.to_int () != 0; }
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -169,24 +194,24 @@ struct trak
{ {
const TrackData &trackData = this+horizData; const TrackData &trackData = this+horizData;
float tracking = trackData.get_tracking (this, ptem); float tracking = trackData.get_tracking (this, ptem);
hb_position_t advance_to_add = c->font->em_scalef_x (tracking / 2); hb_position_t offset_to_add = c->font->em_scalef_x (tracking / 2);
hb_position_t advance_to_add = c->font->em_scalef_x (tracking);
foreach_grapheme (buffer, start, end) foreach_grapheme (buffer, start, end)
{ {
buffer->pos[start].x_offset += advance_to_add;
buffer->pos[start].x_advance += advance_to_add; buffer->pos[start].x_advance += advance_to_add;
buffer->pos[end].x_advance += advance_to_add; buffer->pos[start].x_offset += offset_to_add;
} }
} }
else else
{ {
const TrackData &trackData = this+vertData; const TrackData &trackData = this+vertData;
float tracking = trackData.get_tracking (this, ptem); float tracking = trackData.get_tracking (this, ptem);
hb_position_t advance_to_add = c->font->em_scalef_y (tracking / 2); hb_position_t offset_to_add = c->font->em_scalef_y (tracking / 2);
hb_position_t advance_to_add = c->font->em_scalef_y (tracking);
foreach_grapheme (buffer, start, end) foreach_grapheme (buffer, start, end)
{ {
buffer->pos[start].y_offset += advance_to_add;
buffer->pos[start].y_advance += advance_to_add; buffer->pos[start].y_advance += advance_to_add;
buffer->pos[end].y_advance += advance_to_add; buffer->pos[start].y_offset += offset_to_add;
} }
} }
@ -194,15 +219,17 @@ struct trak
} }
protected: protected:
FixedVersion<> version; /* Version of the tracking table--currently FixedVersion<> version; /* Version of the tracking table
* 0x00010000u for version 1.0. */ * (0x00010000u for version 1.0). */
HBUINT16 format; /* Format of the tracking table */ HBUINT16 format; /* Format of the tracking table (set to 0). */
OffsetTo<TrackData> horizData; /* TrackData for horizontal text */ OffsetTo<TrackData> horizData; /* Offset from start of tracking table to TrackData
OffsetTo<TrackData> vertData; /* TrackData for vertical text */ * for horizontal text (or 0 if none). */
OffsetTo<TrackData> vertData; /* Offset from start of tracking table to TrackData
* for vertical text (or 0 if none). */
HBUINT16 reserved; /* Reserved. Set to 0. */ HBUINT16 reserved; /* Reserved. Set to 0. */
public: public:
DEFINE_SIZE_MIN (12); DEFINE_SIZE_STATIC (12);
}; };
} /* namespace AAT */ } /* namespace AAT */

View File

@ -37,7 +37,7 @@
#include "hb-aat-ltag-table.hh" // Just so we compile it; unused otherwise. #include "hb-aat-ltag-table.hh" // Just so we compile it; unused otherwise.
/* /*
* morx/kerx/trak/ankr * morx/kerx/trak
*/ */
static inline const AAT::morx& static inline const AAT::morx&
@ -68,6 +68,26 @@ _get_kerx (hb_face_t *face, hb_blob_t **blob = nullptr)
*blob = hb_ot_face_data (face)->kerx.get_blob (); *blob = hb_ot_face_data (face)->kerx.get_blob ();
return kerx; return kerx;
} }
static inline const AAT::ankr&
_get_ankr (hb_face_t *face, hb_blob_t **blob = nullptr)
{
if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
{
if (blob)
*blob = hb_blob_get_empty ();
return Null(AAT::ankr);
}
const AAT::ankr& ankr = *(hb_ot_face_data (face)->ankr.get ());
if (blob)
*blob = hb_ot_face_data (face)->ankr.get_blob ();
return ankr;
}
static inline const AAT::trak&
_get_trak (hb_face_t *face)
{
if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(AAT::trak);
return *(hb_ot_face_data (face)->trak.get ());
}
hb_bool_t hb_bool_t
@ -103,6 +123,27 @@ hb_aat_layout_position (hb_ot_shape_plan_t *plan,
hb_blob_t *blob; hb_blob_t *blob;
const AAT::kerx& kerx = _get_kerx (font->face, &blob); const AAT::kerx& kerx = _get_kerx (font->face, &blob);
AAT::hb_aat_apply_context_t c (plan, font, buffer, blob); hb_blob_t *ankr_blob;
const AAT::ankr& ankr = _get_ankr (font->face, &ankr_blob);
AAT::hb_aat_apply_context_t c (plan, font, buffer, blob,
ankr, ankr_blob->data + ankr_blob->length);
kerx.apply (&c); kerx.apply (&c);
} }
hb_bool_t
hb_aat_layout_has_tracking (hb_face_t *face)
{
return _get_trak (face).has_data ();
}
void
hb_aat_layout_track (hb_ot_shape_plan_t *plan,
hb_font_t *font,
hb_buffer_t *buffer)
{
const AAT::trak& trak = _get_trak (font->face);
AAT::hb_aat_apply_context_t c (plan, font, buffer);
trak.apply (&c);
}

View File

@ -47,4 +47,12 @@ hb_aat_layout_position (hb_ot_shape_plan_t *plan,
hb_font_t *font, hb_font_t *font,
hb_buffer_t *buffer); hb_buffer_t *buffer);
HB_INTERNAL hb_bool_t
hb_aat_layout_has_tracking (hb_face_t *face);
HB_INTERNAL void
hb_aat_layout_track (hb_ot_shape_plan_t *plan,
hb_font_t *font,
hb_buffer_t *buffer);
#endif /* HB_AAT_LAYOUT_HH */ #endif /* HB_AAT_LAYOUT_HH */

View File

@ -1755,10 +1755,6 @@ template <typename context_t>
struct GPOS_accelerator_t : GPOS::accelerator_t {}; struct GPOS_accelerator_t : GPOS::accelerator_t {};
#undef attach_chain
#undef attach_type
} /* namespace OT */ } /* namespace OT */

View File

@ -100,22 +100,23 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan,
else if (hb_aat_layout_has_positioning (face)) else if (hb_aat_layout_has_positioning (face))
plan.apply_kerx = true; plan.apply_kerx = true;
if (plan.requested_kerning) if (plan.requested_kerning && !plan.apply_kerx && !has_gpos_kern)
{
if (plan.apply_kerx)
;/* kerx supercedes kern. */
else if (!has_gpos_kern)
{ {
/* Apparently Apple applies kerx if GPOS kern was not applied. */
if (hb_aat_layout_has_positioning (face))
plan.apply_kerx = true;
if (hb_ot_layout_has_kerning (face)) if (hb_ot_layout_has_kerning (face))
plan.apply_kern = true; plan.apply_kern = true;
else else
plan.fallback_kerning = true; plan.fallback_kerning = true;
} }
}
plan.has_gpos_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k')); plan.has_gpos_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k'));
if (!plan.apply_gpos) if (!plan.apply_gpos && !plan.apply_kerx)
plan.fallback_mark_positioning = true; plan.fallback_mark_positioning = true;
/* Currently we always apply trak. */
plan.apply_trak = hb_aat_layout_has_tracking (face);
} }
@ -820,6 +821,7 @@ hb_ot_position_complex (const hb_ot_shape_context_t *c)
hb_ot_layout_position_start (c->font, c->buffer); hb_ot_layout_position_start (c->font, c->buffer);
if (!c->plan->apply_kerx)
switch (c->plan->shaper->zero_width_marks) switch (c->plan->shaper->zero_width_marks)
{ {
case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY:
@ -837,6 +839,10 @@ hb_ot_position_complex (const hb_ot_shape_context_t *c)
else if (c->plan->apply_kerx) else if (c->plan->apply_kerx)
hb_aat_layout_position (c->plan, c->font, c->buffer); hb_aat_layout_position (c->plan, c->font, c->buffer);
if (c->plan->apply_trak)
hb_aat_layout_track (c->plan, c->font, c->buffer);
if (!c->plan->apply_kerx)
switch (c->plan->shaper->zero_width_marks) switch (c->plan->shaper->zero_width_marks)
{ {
case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE:

View File

@ -50,10 +50,11 @@ struct hb_ot_shape_plan_t
bool fallback_kerning : 1; bool fallback_kerning : 1;
bool fallback_mark_positioning : 1; bool fallback_mark_positioning : 1;
bool apply_morx : 1; bool apply_gpos : 1;
bool apply_kerx : 1; bool apply_kerx : 1;
bool apply_kern : 1; bool apply_kern : 1;
bool apply_gpos : 1; bool apply_morx : 1;
bool apply_trak : 1;
inline void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const inline void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const

View File

@ -1,4 +1,5 @@
TESTS = \ TESTS = \
tests/aat-trak.tests \
tests/arabic-fallback-shaping.tests \ tests/arabic-fallback-shaping.tests \
tests/arabic-feature-order.tests \ tests/arabic-feature-order.tests \
tests/arabic-like-joining.tests \ tests/arabic-like-joining.tests \

Binary file not shown.

View File

@ -0,0 +1,8 @@
../fonts/TestTRAK.ttf::U+0041,U+0042,U+0043:[A.alt=0+1000|B=1+1000|C.alt=2+1000]
../fonts/TestTRAK.ttf:--font-ptem=.5:U+0041,U+0042,U+0043:[A.alt=0@100,0+1200|B=1@100,0+1200|C.alt=2@100,0+1200]
../fonts/TestTRAK.ttf:--font-ptem=1:U+0041,U+0042,U+0043:[A.alt=0@100,0+1200|B=1@100,0+1200|C.alt=2@100,0+1200]
../fonts/TestTRAK.ttf:--font-ptem=2:U+0041,U+0042,U+0043:[A.alt=0@93,0+1187|B=1@93,0+1187|C.alt=2@93,0+1187]
../fonts/TestTRAK.ttf:--font-ptem=9:U+0041,U+0042,U+0043:[A.alt=0+1000|B=1+1000|C.alt=2+1000]
../fonts/TestTRAK.ttf:--font-ptem=24:U+0041,U+0042,U+0043:[A.alt=0@-12,0+976|B=1@-12,0+976|C.alt=2@-12,0+976]
../fonts/TestTRAK.ttf:--font-ptem=72:U+0041,U+0042,U+0043:[A.alt=0@-50,0+900|B=1@-50,0+900|C.alt=2@-50,0+900]
../fonts/TestTRAK.ttf:--font-ptem=144:U+0041,U+0042,U+0043:[A.alt=0@-100,0+800|B=1@-100,0+800|C.alt=2@-100,0+800]