Merge branch 'master' into cff-subset
This commit is contained in:
commit
ce6639cd27
|
@ -45,32 +45,47 @@ struct Anchor
|
|||
return_trace (c->check_struct (this));
|
||||
}
|
||||
|
||||
public:
|
||||
FWORD xCoordinate;
|
||||
FWORD yCoordinate;
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (4);
|
||||
};
|
||||
|
||||
typedef LArrayOf<Anchor> GlyphAnchors;
|
||||
|
||||
struct 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
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (likely (c->check_struct (this) &&
|
||||
version == 0 &&
|
||||
lookupTable.sanitize (c, this) &&
|
||||
anchors.sanitize (c, this)));
|
||||
lookupTable.sanitize (c, this)));
|
||||
}
|
||||
|
||||
protected:
|
||||
HBUINT16 version; /* Version number (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 */
|
||||
LOffsetTo<LArrayOf<Anchor> >
|
||||
anchors; /* Offset to the glyph data table */
|
||||
LOffsetTo<HBUINT8>
|
||||
anchorData; /* Offset to the glyph data table */
|
||||
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (12);
|
||||
|
|
|
@ -514,6 +514,7 @@ struct StateTableDriver
|
|||
};
|
||||
|
||||
|
||||
struct ankr;
|
||||
|
||||
struct hb_aat_apply_context_t :
|
||||
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_buffer_t *buffer;
|
||||
hb_sanitize_context_t sanitizer;
|
||||
const ankr &ankr_table;
|
||||
const char *ankr_end;
|
||||
|
||||
/* Unused. For debug tracing only. */
|
||||
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_,
|
||||
hb_font_t *font_,
|
||||
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_),
|
||||
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.start_processing ();
|
||||
sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX);
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "hb-open-type.hh"
|
||||
#include "hb-aat-layout-common.hh"
|
||||
#include "hb-ot-layout-gpos-table.hh"
|
||||
#include "hb-ot-kern-table.hh"
|
||||
|
||||
/*
|
||||
|
@ -99,6 +100,100 @@ struct KerxSubTableFormat0
|
|||
|
||||
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
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
|
@ -106,7 +201,10 @@ struct KerxSubTableFormat1
|
|||
if (!c->plan->requested_kerning)
|
||||
return false;
|
||||
|
||||
/* TODO */
|
||||
driver_context_t dc (this, c);
|
||||
|
||||
StateTableDriver<EntryData> driver (machine, c->buffer, c->font->face);
|
||||
driver.drive (&dc);
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
@ -114,14 +212,13 @@ struct KerxSubTableFormat1
|
|||
inline bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (likely (c->check_struct (this) &&
|
||||
stateHeader.sanitize (c)));
|
||||
return_trace (likely (machine.sanitize (c)));
|
||||
}
|
||||
|
||||
protected:
|
||||
KerxSubTableHeader header;
|
||||
StateTable<HBUINT16> stateHeader;
|
||||
LOffsetTo<ArrayOf<HBUINT16> > valueTable;
|
||||
StateTable<EntryData> machine;
|
||||
LOffsetTo<UnsizedArrayOf<FWORD>, false> kernAction;
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (32);
|
||||
};
|
||||
|
@ -136,7 +233,7 @@ struct KerxSubTableFormat2
|
|||
unsigned int offset = l + r;
|
||||
const FWORD *v = &StructAtOffset<FWORD> (&(this+array), offset);
|
||||
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 *v;
|
||||
}
|
||||
|
@ -159,11 +256,9 @@ struct KerxSubTableFormat2
|
|||
inline bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (likely (c->check_struct (this) &&
|
||||
rowWidth.sanitize (c) &&
|
||||
return_trace (likely (rowWidth.sanitize (c) &&
|
||||
leftClassTable.sanitize (c, this) &&
|
||||
rightClassTable.sanitize (c, this) &&
|
||||
array.sanitize (c, this)));
|
||||
rightClassTable.sanitize (c, this)));
|
||||
}
|
||||
|
||||
struct accelerator_t
|
||||
|
@ -190,7 +285,8 @@ struct KerxSubTableFormat2
|
|||
LOffsetTo<Lookup<HBUINT16> >
|
||||
rightClassTable;/* Offset from beginning of this subtable to
|
||||
* 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. */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (28);
|
||||
|
@ -198,11 +294,152 @@ struct KerxSubTableFormat2
|
|||
|
||||
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
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
@ -211,14 +448,18 @@ struct KerxSubTableFormat4
|
|||
{
|
||||
TRACE_SANITIZE (this);
|
||||
|
||||
/* TODO */
|
||||
return_trace (likely (c->check_struct (this)));
|
||||
/* The rest of array sanitizations are done at run-time. */
|
||||
return_trace (c->check_struct (this) &&
|
||||
machine.sanitize (c) &&
|
||||
flags.sanitize (c));
|
||||
}
|
||||
|
||||
protected:
|
||||
KerxSubTableHeader header;
|
||||
StateTable<EntryData> machine;
|
||||
HBUINT32 flags;
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (12);
|
||||
DEFINE_SIZE_STATIC (32);
|
||||
};
|
||||
|
||||
struct KerxSubTableFormat6
|
||||
|
@ -241,7 +482,7 @@ struct KerxSubTableFormat6
|
|||
unsigned int offset = l + r;
|
||||
const FWORD32 *v = &StructAtOffset<FWORD32> (&(this+t.array), offset * sizeof (FWORD32));
|
||||
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 *v;
|
||||
}
|
||||
|
@ -253,7 +494,7 @@ struct KerxSubTableFormat6
|
|||
unsigned int offset = l + r;
|
||||
const FWORD *v = &StructAtOffset<FWORD> (&(this+t.array), offset * sizeof (FWORD));
|
||||
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 *v;
|
||||
}
|
||||
|
@ -281,12 +522,10 @@ struct KerxSubTableFormat6
|
|||
is_long () ?
|
||||
(
|
||||
u.l.rowIndexTable.sanitize (c, this) &&
|
||||
u.l.columnIndexTable.sanitize (c, this) &&
|
||||
u.l.array.sanitize (c, this)
|
||||
u.l.columnIndexTable.sanitize (c, this)
|
||||
) : (
|
||||
u.s.rowIndexTable.sanitize (c, this) &&
|
||||
u.s.columnIndexTable.sanitize (c, this) &&
|
||||
u.s.array.sanitize (c, this)
|
||||
u.s.columnIndexTable.sanitize (c, this)
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -316,13 +555,15 @@ struct KerxSubTableFormat6
|
|||
{
|
||||
LOffsetTo<Lookup<HBUINT32> > rowIndexTable;
|
||||
LOffsetTo<Lookup<HBUINT32> > columnIndexTable;
|
||||
LOffsetTo<FWORD32> array;
|
||||
LOffsetTo<UnsizedArrayOf<FWORD32>, false>
|
||||
array;
|
||||
} l;
|
||||
struct Short
|
||||
{
|
||||
LOffsetTo<Lookup<HBUINT16> > rowIndexTable;
|
||||
LOffsetTo<Lookup<HBUINT16> > columnIndexTable;
|
||||
LOffsetTo<FWORD> array;
|
||||
LOffsetTo<UnsizedArrayOf<FWORD>, false>
|
||||
array;
|
||||
} s;
|
||||
} u;
|
||||
public:
|
||||
|
|
|
@ -164,7 +164,7 @@ struct RearrangementSubtable
|
|||
|
||||
driver_context_t dc (this);
|
||||
|
||||
StateTableDriver<void> driver (machine, c->buffer, c->face);
|
||||
StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
|
||||
driver.drive (&dc);
|
||||
|
||||
return_trace (dc.ret);
|
||||
|
@ -365,7 +365,7 @@ struct LigatureSubtable
|
|||
inline bool is_actionable (StateTableDriver<EntryData> *driver,
|
||||
const Entry<EntryData> *entry)
|
||||
{
|
||||
return !!(entry->flags & PerformAction);
|
||||
return entry->flags & PerformAction;
|
||||
}
|
||||
inline bool transition (StateTableDriver<EntryData> *driver,
|
||||
const Entry<EntryData> *entry)
|
||||
|
|
|
@ -46,28 +46,32 @@ struct TrackTableEntry
|
|||
{
|
||||
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
|
||||
{
|
||||
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:
|
||||
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>
|
||||
valuesZ; /* Offset from start of tracking table to
|
||||
* per-size tracking values for this track. */
|
||||
|
@ -78,12 +82,20 @@ struct TrackTableEntry
|
|||
|
||||
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);
|
||||
return_trace (c->check_struct (this) &&
|
||||
sizeTable.sanitize (c, base, nSizes) &&
|
||||
trackTable.sanitize (c, nTracks, base, nSizes));
|
||||
unsigned int sizes = nSizes;
|
||||
hb_array_t<Fixed> size_table ((base+sizeTable).arrayZ, sizes);
|
||||
|
||||
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
|
||||
|
@ -94,48 +106,59 @@ struct TrackData
|
|||
* https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html
|
||||
*/
|
||||
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;
|
||||
for (unsigned int i = 0; i < sizes; ++i)
|
||||
// For now we only seek for track entries with zero tracking value
|
||||
if (trackTable[i].get_track_value () == 0.f)
|
||||
trackTableEntry = &trackTable[0];
|
||||
unsigned int count = nTracks;
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
{
|
||||
/* 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.;
|
||||
|
||||
/*
|
||||
* Choose size.
|
||||
*/
|
||||
unsigned int sizes = nSizes;
|
||||
if (!sizes) return 0.;
|
||||
if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
|
||||
|
||||
/* TODO bfind() */
|
||||
hb_array_t<Fixed> size_table ((base+sizeTable).arrayZ, sizes);
|
||||
unsigned int size_index;
|
||||
UnsizedArrayOf<Fixed> size_table = base+sizeTable;
|
||||
for (size_index = 0; size_index < sizes; ++size_index)
|
||||
if (size_table[size_index] >= fixed_size)
|
||||
for (size_index = 0; size_index < sizes; size_index++)
|
||||
if (size_table[size_index].to_float () >= csspx)
|
||||
break;
|
||||
|
||||
// TODO(ebraminio): We don't attempt to extrapolate to larger or
|
||||
// smaller values for now but we should do, per spec
|
||||
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);
|
||||
return interpolate_at (size_index ? size_index - 1 : 0, csspx,
|
||||
*trackTableEntry, base);
|
||||
}
|
||||
|
||||
float s0 = size_table[size_index - 1].to_float ();
|
||||
float s1 = size_table[size_index].to_float ();
|
||||
float t = (csspx - s0) / (s1 - s0);
|
||||
return (float) t * trackTableEntry->get_value (base, size_index) +
|
||||
((float) 1.0 - t) * trackTableEntry->get_value (base, size_index - 1);
|
||||
inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this) &&
|
||||
sizeTable.sanitize (c, base, nSizes) &&
|
||||
trackTable.sanitize (c, nTracks, base, nSizes));
|
||||
}
|
||||
|
||||
protected:
|
||||
HBUINT16 nTracks; /* Number of separate tracks included in this table. */
|
||||
HBUINT16 nSizes; /* Number of point sizes included in this table. */
|
||||
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>
|
||||
trackTable; /* Array[nTracks] of TrackTableEntry records. */
|
||||
|
||||
|
@ -147,6 +170,8 @@ struct 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
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
|
@ -169,24 +194,24 @@ struct trak
|
|||
{
|
||||
const TrackData &trackData = this+horizData;
|
||||
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)
|
||||
{
|
||||
buffer->pos[start].x_offset += 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
|
||||
{
|
||||
const TrackData &trackData = this+vertData;
|
||||
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)
|
||||
{
|
||||
buffer->pos[start].y_offset += 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:
|
||||
FixedVersion<> version; /* Version of the tracking table--currently
|
||||
* 0x00010000u for version 1.0. */
|
||||
HBUINT16 format; /* Format of the tracking table */
|
||||
OffsetTo<TrackData> horizData; /* TrackData for horizontal text */
|
||||
OffsetTo<TrackData> vertData; /* TrackData for vertical text */
|
||||
FixedVersion<> version; /* Version of the tracking table
|
||||
* (0x00010000u for version 1.0). */
|
||||
HBUINT16 format; /* Format of the tracking table (set to 0). */
|
||||
OffsetTo<TrackData> horizData; /* Offset from start of tracking table to TrackData
|
||||
* 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. */
|
||||
|
||||
public:
|
||||
DEFINE_SIZE_MIN (12);
|
||||
DEFINE_SIZE_STATIC (12);
|
||||
};
|
||||
|
||||
} /* namespace AAT */
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
#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&
|
||||
|
@ -68,6 +68,26 @@ _get_kerx (hb_face_t *face, hb_blob_t **blob = nullptr)
|
|||
*blob = hb_ot_face_data (face)->kerx.get_blob ();
|
||||
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
|
||||
|
@ -103,6 +123,27 @@ hb_aat_layout_position (hb_ot_shape_plan_t *plan,
|
|||
hb_blob_t *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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -47,4 +47,12 @@ hb_aat_layout_position (hb_ot_shape_plan_t *plan,
|
|||
hb_font_t *font,
|
||||
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 */
|
||||
|
|
|
@ -1755,10 +1755,6 @@ template <typename context_t>
|
|||
struct GPOS_accelerator_t : GPOS::accelerator_t {};
|
||||
|
||||
|
||||
#undef attach_chain
|
||||
#undef attach_type
|
||||
|
||||
|
||||
} /* namespace OT */
|
||||
|
||||
|
||||
|
|
|
@ -100,22 +100,23 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan,
|
|||
else if (hb_aat_layout_has_positioning (face))
|
||||
plan.apply_kerx = true;
|
||||
|
||||
if (plan.requested_kerning)
|
||||
{
|
||||
if (plan.apply_kerx)
|
||||
;/* kerx supercedes kern. */
|
||||
else if (!has_gpos_kern)
|
||||
if (plan.requested_kerning && !plan.apply_kerx && !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))
|
||||
plan.apply_kern = true;
|
||||
else
|
||||
plan.fallback_kerning = true;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
|
||||
if (!c->plan->apply_kerx)
|
||||
switch (c->plan->shaper->zero_width_marks)
|
||||
{
|
||||
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)
|
||||
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)
|
||||
{
|
||||
case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE:
|
||||
|
|
|
@ -50,10 +50,11 @@ struct hb_ot_shape_plan_t
|
|||
bool fallback_kerning : 1;
|
||||
bool fallback_mark_positioning : 1;
|
||||
|
||||
bool apply_morx : 1;
|
||||
bool apply_gpos : 1;
|
||||
bool apply_kerx : 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
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
TESTS = \
|
||||
tests/aat-trak.tests \
|
||||
tests/arabic-fallback-shaping.tests \
|
||||
tests/arabic-feature-order.tests \
|
||||
tests/arabic-like-joining.tests \
|
||||
|
|
Binary file not shown.
|
@ -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]
|
Loading…
Reference in New Issue