[kern] Implement CrossStream kerning

This commit is contained in:
Behdad Esfahbod 2018-11-06 14:48:42 -05:00
parent b11830c09e
commit 01bf43ac01
2 changed files with 101 additions and 37 deletions

View File

@ -62,6 +62,20 @@ kerxTupleKern (int value,
struct KerxSubTableHeader struct KerxSubTableHeader
{ {
enum Coverage
{
Vertical = 0x80000000, /* Set if table has vertical kerning values. */
CrossStream = 0x40000000, /* Set if table has cross-stream kerning values. */
Variation = 0x20000000, /* Set if table has variation kerning values. */
Backwards = 0x10000000, /* If clear, process the glyphs forwards, that
* is, from first to last in the glyph stream.
* If we, process them from last to first.
* This flag only applies to state-table based
* 'kerx' subtables (types 1 and 4). */
Reserved = 0x0FFFFF00, /* Reserved, set to zero. */
SubtableType = 0x000000FF, /* Subtable type. */
};
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -95,6 +109,9 @@ struct KerxSubTableFormat0
if (!c->plan->requested_kerning) if (!c->plan->requested_kerning)
return false; return false;
if (header.coverage & header.CrossStream)
return false;
accelerator_t accel (*this, c); accelerator_t accel (*this, c);
hb_kern_machine_t<accelerator_t> machine (accel); hb_kern_machine_t<accelerator_t> machine (accel);
machine.kern (c->font, c->buffer, c->plan->kern_mask); machine.kern (c->font, c->buffer, c->plan->kern_mask);
@ -161,7 +178,8 @@ struct KerxSubTableFormat1
* similar to offsets in morx table, NOT from beginning of this table, like * similar to offsets in morx table, NOT from beginning of this table, like
* other subtables in kerx. Discovered via testing. */ * other subtables in kerx. Discovered via testing. */
kernAction (&table->machine + table->kernAction), kernAction (&table->machine + table->kernAction),
depth (0) {} depth (0),
crossStream (table->header.coverage & table->header.CrossStream) {}
inline bool is_actionable (StateTableDriver<MorxTypes, EntryData> *driver HB_UNUSED, inline bool is_actionable (StateTableDriver<MorxTypes, EntryData> *driver HB_UNUSED,
const Entry<EntryData> *entry) const Entry<EntryData> *entry)
@ -209,15 +227,25 @@ struct KerxSubTableFormat1
{ {
if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
{ {
buffer->pos[idx].x_advance += c->font->em_scale_x (v); if (crossStream)
if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) buffer->pos[idx].y_offset += c->font->em_scale_y (v);
buffer->pos[idx].x_offset += c->font->em_scale_x (v); else
{
buffer->pos[idx].x_advance += c->font->em_scale_x (v);
if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
buffer->pos[idx].x_offset += c->font->em_scale_x (v);
}
} }
else else
{ {
buffer->pos[idx].y_advance += c->font->em_scale_y (v); if (crossStream)
if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) buffer->pos[idx].x_offset += c->font->em_scale_x (v);
buffer->pos[idx].y_offset += c->font->em_scale_y (v); else
{
buffer->pos[idx].y_advance += c->font->em_scale_y (v);
if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
buffer->pos[idx].y_offset += c->font->em_scale_y (v);
}
} }
} }
} }
@ -232,6 +260,7 @@ struct KerxSubTableFormat1
const UnsizedArrayOf<FWORD> &kernAction; const UnsizedArrayOf<FWORD> &kernAction;
unsigned int stack[8]; unsigned int stack[8];
unsigned int depth; unsigned int depth;
bool crossStream;
}; };
inline bool apply (hb_aat_apply_context_t *c) const inline bool apply (hb_aat_apply_context_t *c) const
@ -241,6 +270,9 @@ struct KerxSubTableFormat1
if (!c->plan->requested_kerning) if (!c->plan->requested_kerning)
return false; return false;
if (header.coverage & header.CrossStream)
return false;
if (header.tupleCount) if (header.tupleCount)
return_trace (false); /* TODO kerxTupleKern */ return_trace (false); /* TODO kerxTupleKern */
@ -289,6 +321,9 @@ struct KerxSubTableFormat2
if (!c->plan->requested_kerning) if (!c->plan->requested_kerning)
return false; return false;
if (header.coverage & header.CrossStream)
return false;
accelerator_t accel (*this, c); accelerator_t accel (*this, c);
hb_kern_machine_t<accelerator_t> machine (accel); hb_kern_machine_t<accelerator_t> machine (accel);
machine.kern (c->font, c->buffer, c->plan->kern_mask); machine.kern (c->font, c->buffer, c->plan->kern_mask);
@ -547,6 +582,9 @@ struct KerxSubTableFormat6
if (!c->plan->requested_kerning) if (!c->plan->requested_kerning)
return false; return false;
if (header.coverage & header.CrossStream)
return false;
accelerator_t accel (*this, c); accelerator_t accel (*this, c);
hb_kern_machine_t<accelerator_t> machine (accel); hb_kern_machine_t<accelerator_t> machine (accel);
machine.kern (c->font, c->buffer, c->plan->kern_mask); machine.kern (c->font, c->buffer, c->plan->kern_mask);
@ -615,21 +653,7 @@ struct KerxTable
friend struct kerx; friend struct kerx;
inline unsigned int get_size (void) const { return u.header.length; } inline unsigned int get_size (void) const { return u.header.length; }
inline unsigned int get_type (void) const { return u.header.coverage & SubtableType; } inline unsigned int get_type (void) const { return u.header.coverage & u.header.SubtableType; }
enum Coverage
{
Vertical = 0x80000000, /* Set if table has vertical kerning values. */
CrossStream = 0x40000000, /* Set if table has cross-stream kerning values. */
Variation = 0x20000000, /* Set if table has variation kerning values. */
Backwards = 0x10000000, /* If clear, process the glyphs forwards, that
* is, from first to last in the glyph stream.
* If we, process them from last to first.
* This flag only applies to state-table based
* 'kerx' subtables (types 1 and 4). */
Reserved = 0x0FFFFF00, /* Reserved, set to zero. */
SubtableType = 0x000000FF, /* Subtable type. */
};
template <typename context_t> template <typename context_t>
inline typename context_t::return_t dispatch (context_t *c) const inline typename context_t::return_t dispatch (context_t *c) const
@ -689,14 +713,11 @@ struct kerx
{ {
bool reverse; bool reverse;
if (table->u.header.coverage & (KerxTable::CrossStream))
goto skip; /* We do NOT handle cross-stream. */
if (HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) != if (HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
bool (table->u.header.coverage & KerxTable::Vertical)) bool (table->u.header.coverage & table->u.header.Vertical))
goto skip; goto skip;
reverse = bool (table->u.header.coverage & KerxTable::Backwards) != reverse = bool (table->u.header.coverage & table->u.header.Backwards) !=
HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
if (!c->buffer->message (c->font, "start kerx subtable %d", c->lookup_index)) if (!c->buffer->message (c->font, "start kerx subtable %d", c->lookup_index))

View File

@ -174,6 +174,9 @@ struct KernSubTableFormat0
if (!c->plan->requested_kerning) if (!c->plan->requested_kerning)
return false; return false;
if (header.coverage & header.CrossStream)
return false;
hb_kern_machine_t<KernSubTableFormat0> machine (*this); hb_kern_machine_t<KernSubTableFormat0> machine (*this);
machine.kern (c->font, c->buffer, c->plan->kern_mask); machine.kern (c->font, c->buffer, c->plan->kern_mask);
@ -218,7 +221,9 @@ struct KernSubTableFormat1
* similar to offsets in morx table, NOT from beginning of this table, like * similar to offsets in morx table, NOT from beginning of this table, like
* other subtables in kerx. Discovered via testing. */ * other subtables in kerx. Discovered via testing. */
kernAction (&table->machine + table->kernAction), kernAction (&table->machine + table->kernAction),
depth (0) {} depth (0),
crossStream (table->header.coverage & table->header.CrossStream),
crossOffset (0) {}
inline bool is_actionable (AAT::StateTableDriver<AAT::MortTypes, EntryData> *driver HB_UNUSED, inline bool is_actionable (AAT::StateTableDriver<AAT::MortTypes, EntryData> *driver HB_UNUSED,
const AAT::Entry<EntryData> *entry) const AAT::Entry<EntryData> *entry)
@ -241,7 +246,9 @@ struct KernSubTableFormat1
if (entry->flags & Offset) if (entry->flags & Offset)
{ {
unsigned int kernIndex = AAT::MortTypes::offsetToIndex (entry->flags & Offset, &table->machine, kernAction.arrayZ); unsigned int kernIndex = AAT::MortTypes::offsetToIndex (entry->flags & Offset,
&table->machine,
kernAction.arrayZ);
const FWORD *actions = &kernAction[kernIndex]; const FWORD *actions = &kernAction[kernIndex];
if (!c->sanitizer.check_array (actions, depth)) if (!c->sanitizer.check_array (actions, depth))
{ {
@ -258,21 +265,50 @@ struct KernSubTableFormat1
* list. Discovered by testing. */ * list. Discovered by testing. */
unsigned int idx = stack[i]; unsigned int idx = stack[i];
int v = *actions++; int v = *actions++;
/* The following two flags are undocumented in the spec, but described
* in the example. */
bool last = v & 1;
v = v & ~1;
if (v == 0x8000)
{
crossOffset = 0;
v = 0;
}
if (idx < buffer->len && buffer->info[idx].mask & kern_mask) if (idx < buffer->len && buffer->info[idx].mask & kern_mask)
{ {
if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
{ {
buffer->pos[idx].x_advance += c->font->em_scale_x (v); if (crossStream)
if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) {
buffer->pos[idx].x_offset += c->font->em_scale_x (v); crossOffset += v;
buffer->pos[idx].y_offset += c->font->em_scale_y (crossOffset);
}
else
{
buffer->pos[idx].x_advance += c->font->em_scale_x (v);
if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
buffer->pos[idx].x_offset += c->font->em_scale_x (v);
}
} }
else else
{ {
buffer->pos[idx].y_advance += c->font->em_scale_y (v); if (crossStream)
if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) {
buffer->pos[idx].y_offset += c->font->em_scale_y (v); crossOffset += v;
buffer->pos[idx].x_offset += c->font->em_scale_x (crossOffset);
}
else
{
buffer->pos[idx].y_advance += c->font->em_scale_y (v);
if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
buffer->pos[idx].y_offset += c->font->em_scale_y (v);
}
} }
} }
if (last)
break;
} }
depth = 0; depth = 0;
} }
@ -286,6 +322,8 @@ struct KernSubTableFormat1
const UnsizedArrayOf<FWORD> &kernAction; const UnsizedArrayOf<FWORD> &kernAction;
unsigned int stack[8]; unsigned int stack[8];
unsigned int depth; unsigned int depth;
bool crossStream;
int crossOffset;
}; };
inline bool apply (AAT::hb_aat_apply_context_t *c) const inline bool apply (AAT::hb_aat_apply_context_t *c) const
@ -344,6 +382,9 @@ struct KernSubTableFormat2
if (!c->plan->requested_kerning) if (!c->plan->requested_kerning)
return false; return false;
if (header.coverage & header.CrossStream)
return false;
accelerator_t accel (*this, c); accelerator_t accel (*this, c);
hb_kern_machine_t<accelerator_t> machine (accel); hb_kern_machine_t<accelerator_t> machine (accel);
machine.kern (c->font, c->buffer, c->plan->kern_mask); machine.kern (c->font, c->buffer, c->plan->kern_mask);
@ -412,6 +453,9 @@ struct KernSubTableFormat3
if (!c->plan->requested_kerning) if (!c->plan->requested_kerning)
return false; return false;
if (header.coverage & header.CrossStream)
return false;
hb_kern_machine_t<KernSubTableFormat3> machine (*this); hb_kern_machine_t<KernSubTableFormat3> machine (*this);
machine.kern (c->font, c->buffer, c->plan->kern_mask); machine.kern (c->font, c->buffer, c->plan->kern_mask);
@ -547,8 +591,7 @@ struct KernTable
st = CastP<SubTable> (&thiz()->dataZ); st = CastP<SubTable> (&thiz()->dataZ);
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
{ {
if (st->u.header.coverage & if (st->u.header.coverage & st->u.header.Variation)
(st->u.header.Variation | st->u.header.CrossStream))
goto skip; goto skip;
if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) !=