2017-08-15 02:24:36 +02:00
|
|
|
/*
|
|
|
|
* Copyright © 2017 Google, Inc.
|
|
|
|
*
|
|
|
|
* This is part of HarfBuzz, a text shaping library.
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, without written agreement and without
|
|
|
|
* license or royalty fees, to use, copy, modify, and distribute this
|
|
|
|
* software and its documentation for any purpose, provided that the
|
|
|
|
* above copyright notice and the following two paragraphs appear in
|
|
|
|
* all copies of this software.
|
|
|
|
*
|
|
|
|
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
|
|
|
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
|
|
|
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
|
|
|
* DAMAGE.
|
|
|
|
*
|
|
|
|
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
|
|
|
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
|
|
|
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
|
|
|
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|
|
|
*
|
|
|
|
* Google Author(s): Behdad Esfahbod
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef HB_AAT_LAYOUT_MORX_TABLE_HH
|
|
|
|
#define HB_AAT_LAYOUT_MORX_TABLE_HH
|
|
|
|
|
2018-08-26 07:36:36 +02:00
|
|
|
#include "hb-open-type.hh"
|
|
|
|
#include "hb-aat-layout-common.hh"
|
|
|
|
#include "hb-ot-layout-common.hh"
|
2017-08-15 02:24:36 +02:00
|
|
|
|
2018-04-12 11:08:19 +02:00
|
|
|
/*
|
|
|
|
* morx -- Extended Glyph Metamorphosis
|
|
|
|
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html
|
|
|
|
*/
|
2018-03-26 09:34:30 +02:00
|
|
|
#define HB_AAT_TAG_morx HB_TAG('m','o','r','x')
|
2017-08-15 02:24:36 +02:00
|
|
|
|
|
|
|
|
|
|
|
namespace AAT {
|
|
|
|
|
|
|
|
using namespace OT;
|
|
|
|
|
|
|
|
|
|
|
|
struct RearrangementSubtable
|
|
|
|
{
|
2018-02-13 22:59:39 +01:00
|
|
|
typedef void EntryData;
|
|
|
|
|
2018-01-12 00:08:22 +01:00
|
|
|
struct driver_context_t
|
2018-01-09 17:55:17 +01:00
|
|
|
{
|
2018-01-12 11:42:25 +01:00
|
|
|
static const bool in_place = true;
|
2018-09-15 14:47:18 +02:00
|
|
|
enum Flags
|
|
|
|
{
|
2018-01-12 09:45:42 +01:00
|
|
|
MarkFirst = 0x8000, /* If set, make the current glyph the first
|
|
|
|
* glyph to be rearranged. */
|
2018-01-12 00:08:22 +01:00
|
|
|
DontAdvance = 0x4000, /* If set, don't advance to the next glyph
|
2018-01-12 09:45:42 +01:00
|
|
|
* before going to the new state. This means
|
|
|
|
* that the glyph index doesn't change, even
|
|
|
|
* if the glyph at that index has changed. */
|
|
|
|
MarkLast = 0x2000, /* If set, make the current glyph the last
|
|
|
|
* glyph to be rearranged. */
|
|
|
|
Reserved = 0x1FF0, /* These bits are reserved and should be set to 0. */
|
|
|
|
Verb = 0x000F, /* The type of rearrangement specified. */
|
2018-01-12 00:08:22 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
inline driver_context_t (const RearrangementSubtable *table) :
|
|
|
|
ret (false),
|
2018-02-04 20:58:02 +01:00
|
|
|
start (0), end (0) {}
|
2018-01-12 00:08:22 +01:00
|
|
|
|
2018-02-13 22:59:39 +01:00
|
|
|
inline bool is_actionable (StateTableDriver<EntryData> *driver,
|
|
|
|
const Entry<EntryData> *entry)
|
2018-02-04 20:58:02 +01:00
|
|
|
{
|
|
|
|
return (entry->flags & Verb) && start < end;
|
|
|
|
}
|
2018-02-13 22:59:39 +01:00
|
|
|
inline bool transition (StateTableDriver<EntryData> *driver,
|
|
|
|
const Entry<EntryData> *entry)
|
2018-01-11 09:15:34 +01:00
|
|
|
{
|
2018-01-12 00:08:22 +01:00
|
|
|
hb_buffer_t *buffer = driver->buffer;
|
2018-01-11 09:15:34 +01:00
|
|
|
unsigned int flags = entry->flags;
|
|
|
|
|
|
|
|
if (flags & MarkFirst)
|
2018-01-12 00:08:22 +01:00
|
|
|
start = buffer->idx;
|
2018-01-11 09:15:34 +01:00
|
|
|
|
2018-01-11 15:23:24 +01:00
|
|
|
if (flags & MarkLast)
|
2018-01-12 00:08:22 +01:00
|
|
|
end = MIN (buffer->idx + 1, buffer->len);
|
2018-01-11 09:15:34 +01:00
|
|
|
|
|
|
|
if ((flags & Verb) && start < end)
|
|
|
|
{
|
|
|
|
/* The following map has two nibbles, for start-side
|
|
|
|
* and end-side. Values of 0,1,2 mean move that many
|
|
|
|
* to the other side. Value of 3 means move 2 and
|
|
|
|
* flip them. */
|
2018-01-12 11:31:20 +01:00
|
|
|
const unsigned char map[16] =
|
2018-01-11 09:15:34 +01:00
|
|
|
{
|
|
|
|
0x00, /* 0 no change */
|
|
|
|
0x10, /* 1 Ax => xA */
|
|
|
|
0x01, /* 2 xD => Dx */
|
|
|
|
0x11, /* 3 AxD => DxA */
|
|
|
|
0x20, /* 4 ABx => xAB */
|
|
|
|
0x30, /* 5 ABx => xBA */
|
|
|
|
0x02, /* 6 xCD => CDx */
|
|
|
|
0x03, /* 7 xCD => DCx */
|
|
|
|
0x12, /* 8 AxCD => CDxA */
|
|
|
|
0x13, /* 9 AxCD => DCxA */
|
|
|
|
0x21, /* 10 ABxD => DxAB */
|
|
|
|
0x31, /* 11 ABxD => DxBA */
|
|
|
|
0x22, /* 12 ABxCD => CDxAB */
|
|
|
|
0x32, /* 13 ABxCD => CDxBA */
|
|
|
|
0x23, /* 14 ABxCD => DCxAB */
|
|
|
|
0x33, /* 15 ABxCD => DCxBA */
|
|
|
|
};
|
|
|
|
|
|
|
|
unsigned int m = map[flags & Verb];
|
|
|
|
unsigned int l = MIN<unsigned int> (2, m >> 4);
|
|
|
|
unsigned int r = MIN<unsigned int> (2, m & 0x0F);
|
|
|
|
bool reverse_l = 3 == (m >> 4);
|
|
|
|
bool reverse_r = 3 == (m & 0x0F);
|
|
|
|
|
|
|
|
if (end - start >= l + r)
|
|
|
|
{
|
2018-02-04 20:58:02 +01:00
|
|
|
buffer->merge_clusters (start, MIN (buffer->idx + 1, buffer->len));
|
2018-01-12 00:08:22 +01:00
|
|
|
buffer->merge_clusters (start, end);
|
2018-01-11 09:15:34 +01:00
|
|
|
|
2018-01-12 00:08:22 +01:00
|
|
|
hb_glyph_info_t *info = buffer->info;
|
2018-01-11 09:15:34 +01:00
|
|
|
hb_glyph_info_t buf[4];
|
2018-01-12 00:08:22 +01:00
|
|
|
|
2018-01-11 09:15:34 +01:00
|
|
|
memcpy (buf, info + start, l * sizeof (buf[0]));
|
|
|
|
memcpy (buf + 2, info + end - r, r * sizeof (buf[0]));
|
|
|
|
|
|
|
|
if (l != r)
|
2018-01-11 10:08:25 +01:00
|
|
|
memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0]));
|
2018-01-11 09:15:34 +01:00
|
|
|
|
|
|
|
memcpy (info + start, buf + 2, r * sizeof (buf[0]));
|
|
|
|
memcpy (info + end - l, buf, l * sizeof (buf[0]));
|
|
|
|
if (reverse_l)
|
|
|
|
{
|
|
|
|
buf[0] = info[end - 1];
|
|
|
|
info[end - 1] = info[end - 2];
|
|
|
|
info[end - 2] = buf[0];
|
|
|
|
}
|
|
|
|
if (reverse_r)
|
|
|
|
{
|
|
|
|
buf[0] = info[start];
|
|
|
|
info[start] = info[start + 1];
|
|
|
|
info[start + 1] = buf[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-01-20 03:08:56 +01:00
|
|
|
|
|
|
|
return true;
|
2018-01-12 00:08:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
bool ret;
|
|
|
|
private:
|
2018-01-12 09:38:55 +01:00
|
|
|
unsigned int start;
|
|
|
|
unsigned int end;
|
2018-01-12 00:08:22 +01:00
|
|
|
};
|
2018-01-11 09:15:34 +01:00
|
|
|
|
2018-01-18 01:59:55 +01:00
|
|
|
inline bool apply (hb_aat_apply_context_t *c) const
|
2018-01-12 00:08:22 +01:00
|
|
|
{
|
|
|
|
TRACE_APPLY (this);
|
2018-01-11 09:15:34 +01:00
|
|
|
|
2018-01-12 00:08:22 +01:00
|
|
|
driver_context_t dc (this);
|
|
|
|
|
|
|
|
StateTableDriver<void> driver (machine, c->buffer, c->face);
|
|
|
|
driver.drive (&dc);
|
2018-01-11 09:15:34 +01:00
|
|
|
|
2018-01-12 00:08:22 +01:00
|
|
|
return_trace (dc.ret);
|
2018-01-09 17:55:17 +01:00
|
|
|
}
|
|
|
|
|
2017-08-15 02:24:36 +02:00
|
|
|
inline bool sanitize (hb_sanitize_context_t *c) const
|
|
|
|
{
|
|
|
|
TRACE_SANITIZE (this);
|
2018-01-11 14:50:47 +01:00
|
|
|
return_trace (machine.sanitize (c));
|
2017-08-15 02:24:36 +02:00
|
|
|
}
|
2018-01-11 09:15:34 +01:00
|
|
|
|
|
|
|
protected:
|
2018-02-13 23:00:14 +01:00
|
|
|
StateTable<EntryData> machine;
|
2018-01-11 09:15:34 +01:00
|
|
|
public:
|
2018-01-12 12:04:53 +01:00
|
|
|
DEFINE_SIZE_STATIC (16);
|
2017-08-15 02:24:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
struct ContextualSubtable
|
|
|
|
{
|
2018-01-11 14:50:47 +01:00
|
|
|
struct EntryData
|
|
|
|
{
|
|
|
|
HBUINT16 markIndex; /* Index of the substitution table for the
|
|
|
|
* marked glyph (use 0xFFFF for none). */
|
|
|
|
HBUINT16 currentIndex; /* Index of the substitution table for the
|
|
|
|
* current glyph (use 0xFFFF for none). */
|
2018-01-11 17:41:34 +01:00
|
|
|
public:
|
2018-01-11 18:01:10 +01:00
|
|
|
DEFINE_SIZE_STATIC (4);
|
2018-01-11 14:50:47 +01:00
|
|
|
};
|
|
|
|
|
2018-01-12 00:01:36 +01:00
|
|
|
struct driver_context_t
|
2018-01-09 17:55:17 +01:00
|
|
|
{
|
2018-01-12 11:42:25 +01:00
|
|
|
static const bool in_place = true;
|
2018-09-15 14:47:18 +02:00
|
|
|
enum Flags
|
|
|
|
{
|
2018-01-12 00:01:36 +01:00
|
|
|
SetMark = 0x8000, /* If set, make the current glyph the marked glyph. */
|
|
|
|
DontAdvance = 0x4000, /* If set, don't advance to the next glyph before
|
|
|
|
* going to the new state. */
|
|
|
|
Reserved = 0x3FFF, /* These bits are reserved and should be set to 0. */
|
|
|
|
};
|
|
|
|
|
|
|
|
inline driver_context_t (const ContextualSubtable *table) :
|
|
|
|
ret (false),
|
2018-02-04 20:45:02 +01:00
|
|
|
mark_set (false),
|
2018-01-12 00:01:36 +01:00
|
|
|
mark (0),
|
|
|
|
subs (table+table->substitutionTables) {}
|
|
|
|
|
2018-02-04 20:58:02 +01:00
|
|
|
inline bool is_actionable (StateTableDriver<EntryData> *driver,
|
|
|
|
const Entry<EntryData> *entry)
|
|
|
|
{
|
|
|
|
hb_buffer_t *buffer = driver->buffer;
|
|
|
|
|
|
|
|
if (buffer->idx == buffer->len && !mark_set)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return entry->data.markIndex != 0xFFFF || entry->data.currentIndex != 0xFFFF;
|
|
|
|
}
|
2018-01-20 03:08:56 +01:00
|
|
|
inline bool transition (StateTableDriver<EntryData> *driver,
|
2018-01-12 00:01:36 +01:00
|
|
|
const Entry<EntryData> *entry)
|
2018-01-11 15:15:07 +01:00
|
|
|
{
|
2018-01-12 00:01:36 +01:00
|
|
|
hb_buffer_t *buffer = driver->buffer;
|
2018-01-11 15:15:07 +01:00
|
|
|
|
2018-02-04 20:45:02 +01:00
|
|
|
/* Looks like CoreText applies neither mark nor current substitution for
|
|
|
|
* end-of-text if mark was not explicitly set. */
|
|
|
|
if (buffer->idx == buffer->len && !mark_set)
|
|
|
|
return true;
|
|
|
|
|
2018-02-04 20:19:41 +01:00
|
|
|
if (entry->data.markIndex != 0xFFFF)
|
2018-01-11 15:15:07 +01:00
|
|
|
{
|
2018-01-11 18:54:49 +01:00
|
|
|
const Lookup<GlyphID> &lookup = subs[entry->data.markIndex];
|
2018-01-12 00:01:36 +01:00
|
|
|
hb_glyph_info_t *info = buffer->info;
|
|
|
|
const GlyphID *replacement = lookup.get_value (info[mark].codepoint, driver->num_glyphs);
|
2018-01-11 15:15:07 +01:00
|
|
|
if (replacement)
|
|
|
|
{
|
2018-02-04 20:58:02 +01:00
|
|
|
buffer->unsafe_to_break (mark, MIN (buffer->idx + 1, buffer->len));
|
2018-01-11 15:15:07 +01:00
|
|
|
info[mark].codepoint = *replacement;
|
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
}
|
2018-02-04 20:40:17 +01:00
|
|
|
if (entry->data.currentIndex != 0xFFFF)
|
2018-01-11 15:15:07 +01:00
|
|
|
{
|
2018-02-04 20:40:17 +01:00
|
|
|
unsigned int idx = MIN (buffer->idx, buffer->len - 1);
|
2018-01-11 18:54:49 +01:00
|
|
|
const Lookup<GlyphID> &lookup = subs[entry->data.currentIndex];
|
2018-01-12 00:01:36 +01:00
|
|
|
hb_glyph_info_t *info = buffer->info;
|
2018-02-04 20:40:17 +01:00
|
|
|
const GlyphID *replacement = lookup.get_value (info[idx].codepoint, driver->num_glyphs);
|
2018-01-11 15:15:07 +01:00
|
|
|
if (replacement)
|
|
|
|
{
|
2018-02-04 20:40:17 +01:00
|
|
|
info[idx].codepoint = *replacement;
|
2018-01-11 15:15:07 +01:00
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
}
|
2018-01-20 03:08:56 +01:00
|
|
|
|
2018-02-02 22:08:50 +01:00
|
|
|
if (entry->flags & SetMark)
|
|
|
|
{
|
2018-02-04 20:45:02 +01:00
|
|
|
mark_set = true;
|
2018-02-02 22:08:50 +01:00
|
|
|
mark = buffer->idx;
|
|
|
|
}
|
|
|
|
|
2018-01-20 03:08:56 +01:00
|
|
|
return true;
|
2018-01-12 00:01:36 +01:00
|
|
|
}
|
2018-01-11 15:15:07 +01:00
|
|
|
|
2018-01-12 00:01:36 +01:00
|
|
|
public:
|
|
|
|
bool ret;
|
|
|
|
private:
|
2018-02-04 20:45:02 +01:00
|
|
|
bool mark_set;
|
2018-01-12 00:01:36 +01:00
|
|
|
unsigned int mark;
|
|
|
|
const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32> &subs;
|
|
|
|
};
|
2018-01-11 15:15:07 +01:00
|
|
|
|
2018-01-18 01:59:55 +01:00
|
|
|
inline bool apply (hb_aat_apply_context_t *c) const
|
2018-01-12 00:01:36 +01:00
|
|
|
{
|
|
|
|
TRACE_APPLY (this);
|
2018-01-11 15:15:07 +01:00
|
|
|
|
2018-01-12 00:01:36 +01:00
|
|
|
driver_context_t dc (this);
|
|
|
|
|
|
|
|
StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
|
|
|
|
driver.drive (&dc);
|
|
|
|
|
|
|
|
return_trace (dc.ret);
|
2018-01-09 17:55:17 +01:00
|
|
|
}
|
|
|
|
|
2017-08-15 02:24:36 +02:00
|
|
|
inline bool sanitize (hb_sanitize_context_t *c) const
|
|
|
|
{
|
|
|
|
TRACE_SANITIZE (this);
|
2018-01-11 18:54:49 +01:00
|
|
|
|
2018-02-07 18:26:41 +01:00
|
|
|
unsigned int num_entries = 0;
|
2018-02-08 22:11:28 +01:00
|
|
|
if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false);
|
2018-01-11 18:54:49 +01:00
|
|
|
|
|
|
|
unsigned int num_lookups = 0;
|
|
|
|
|
|
|
|
const Entry<EntryData> *entries = machine.get_entries ();
|
|
|
|
for (unsigned int i = 0; i < num_entries; i++)
|
|
|
|
{
|
|
|
|
const EntryData &data = entries[i].data;
|
|
|
|
|
2018-02-02 21:53:25 +01:00
|
|
|
if (data.markIndex != 0xFFFF)
|
|
|
|
num_lookups = MAX<unsigned int> (num_lookups, 1 + data.markIndex);
|
|
|
|
if (data.currentIndex != 0xFFFF)
|
|
|
|
num_lookups = MAX<unsigned int> (num_lookups, 1 + data.currentIndex);
|
2018-01-11 18:54:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return_trace (substitutionTables.sanitize (c, this, num_lookups));
|
2017-08-15 02:24:36 +02:00
|
|
|
}
|
2018-01-11 14:50:47 +01:00
|
|
|
|
|
|
|
protected:
|
2018-04-11 16:06:09 +02:00
|
|
|
StateTable<EntryData>
|
|
|
|
machine;
|
2018-09-15 19:43:33 +02:00
|
|
|
LOffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32>, false>
|
2018-04-11 16:06:09 +02:00
|
|
|
substitutionTables;
|
2018-01-11 14:50:47 +01:00
|
|
|
public:
|
2018-01-12 12:04:53 +01:00
|
|
|
DEFINE_SIZE_STATIC (20);
|
2017-08-15 02:24:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
struct LigatureSubtable
|
|
|
|
{
|
2018-01-14 21:03:31 +01:00
|
|
|
struct EntryData
|
|
|
|
{
|
|
|
|
HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry
|
|
|
|
* for processing this group, if indicated
|
|
|
|
* by the flags. */
|
|
|
|
public:
|
|
|
|
DEFINE_SIZE_STATIC (2);
|
|
|
|
};
|
|
|
|
|
|
|
|
struct driver_context_t
|
|
|
|
{
|
|
|
|
static const bool in_place = false;
|
2018-09-15 14:47:18 +02:00
|
|
|
enum Flags
|
|
|
|
{
|
2018-01-14 21:03:31 +01:00
|
|
|
SetComponent = 0x8000, /* Push this glyph onto the component stack for
|
|
|
|
* eventual processing. */
|
|
|
|
DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the
|
|
|
|
next iteration. */
|
|
|
|
PerformAction = 0x2000, /* Use the ligActionIndex to process a ligature
|
|
|
|
* group. */
|
|
|
|
Reserved = 0x1FFF, /* These bits are reserved and should be set to 0. */
|
|
|
|
};
|
2018-09-15 14:47:18 +02:00
|
|
|
enum LigActionFlags
|
|
|
|
{
|
2018-01-20 03:08:56 +01:00
|
|
|
LigActionLast = 0x80000000, /* This is the last action in the list. This also
|
|
|
|
* implies storage. */
|
|
|
|
LigActionStore = 0x40000000, /* Store the ligature at the current cumulated index
|
|
|
|
* in the ligature table in place of the marked
|
|
|
|
* (i.e. currently-popped) glyph. */
|
|
|
|
LigActionOffset = 0x3FFFFFFF, /* A 30-bit value which is sign-extended to 32-bits
|
|
|
|
* and added to the glyph ID, resulting in an index
|
|
|
|
* into the component table. */
|
|
|
|
};
|
2018-01-14 21:03:31 +01:00
|
|
|
|
2018-01-20 03:08:56 +01:00
|
|
|
inline driver_context_t (const LigatureSubtable *table,
|
|
|
|
hb_aat_apply_context_t *c_) :
|
|
|
|
ret (false),
|
|
|
|
c (c_),
|
|
|
|
ligAction (table+table->ligAction),
|
|
|
|
component (table+table->component),
|
|
|
|
ligature (table+table->ligature),
|
|
|
|
match_length (0) {}
|
2018-01-14 21:03:31 +01:00
|
|
|
|
2018-02-04 20:58:02 +01:00
|
|
|
inline bool is_actionable (StateTableDriver<EntryData> *driver,
|
|
|
|
const Entry<EntryData> *entry)
|
|
|
|
{
|
|
|
|
return !!(entry->flags & PerformAction);
|
|
|
|
}
|
2018-01-20 03:08:56 +01:00
|
|
|
inline bool transition (StateTableDriver<EntryData> *driver,
|
2018-01-14 21:03:31 +01:00
|
|
|
const Entry<EntryData> *entry)
|
|
|
|
{
|
|
|
|
hb_buffer_t *buffer = driver->buffer;
|
2018-01-20 03:08:56 +01:00
|
|
|
unsigned int flags = entry->flags;
|
|
|
|
|
|
|
|
if (flags & SetComponent)
|
|
|
|
{
|
|
|
|
if (unlikely (match_length >= ARRAY_LENGTH (match_positions)))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Never mark same index twice, in case DontAdvance was used... */
|
|
|
|
if (match_length && match_positions[match_length - 1] == buffer->out_len)
|
|
|
|
match_length--;
|
|
|
|
|
|
|
|
match_positions[match_length++] = buffer->out_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & PerformAction)
|
|
|
|
{
|
|
|
|
unsigned int end = buffer->out_len;
|
|
|
|
unsigned int action_idx = entry->data.ligActionIndex;
|
|
|
|
unsigned int action;
|
|
|
|
unsigned int ligature_idx = 0;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (unlikely (!match_length))
|
|
|
|
return false;
|
|
|
|
|
2018-01-20 03:36:39 +01:00
|
|
|
buffer->move_to (match_positions[--match_length]);
|
2018-01-14 21:03:31 +01:00
|
|
|
|
2018-01-20 03:08:56 +01:00
|
|
|
const HBUINT32 &actionData = ligAction[action_idx];
|
|
|
|
if (unlikely (!actionData.sanitize (&c->sanitizer))) return false;
|
|
|
|
action = actionData;
|
|
|
|
|
|
|
|
uint32_t uoffset = action & LigActionOffset;
|
|
|
|
if (uoffset & 0x20000000)
|
|
|
|
uoffset += 0xC0000000;
|
|
|
|
int32_t offset = (int32_t) uoffset;
|
|
|
|
unsigned int component_idx = buffer->cur().codepoint + offset;
|
|
|
|
|
|
|
|
const HBUINT16 &componentData = component[component_idx];
|
|
|
|
if (unlikely (!componentData.sanitize (&c->sanitizer))) return false;
|
|
|
|
ligature_idx += componentData;
|
|
|
|
|
|
|
|
if (action & (LigActionStore | LigActionLast))
|
|
|
|
{
|
|
|
|
const GlyphID &ligatureData = ligature[ligature_idx];
|
|
|
|
if (unlikely (!ligatureData.sanitize (&c->sanitizer))) return false;
|
|
|
|
hb_codepoint_t lig = ligatureData;
|
|
|
|
|
2018-01-20 03:36:39 +01:00
|
|
|
match_positions[match_length++] = buffer->out_len;
|
2018-01-20 03:08:56 +01:00
|
|
|
buffer->replace_glyph (lig);
|
|
|
|
|
|
|
|
//ligature_idx = 0; // XXX Yes or no?
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
buffer->skip_glyph ();
|
|
|
|
end--;
|
|
|
|
}
|
2018-01-20 03:24:00 +01:00
|
|
|
/* TODO merge_clusters / unsafe_to_break */
|
2018-01-20 03:08:56 +01:00
|
|
|
|
|
|
|
action_idx++;
|
|
|
|
}
|
|
|
|
while (!(action & LigActionLast));
|
|
|
|
buffer->move_to (end);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2018-01-14 21:03:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
bool ret;
|
|
|
|
private:
|
2018-01-20 03:08:56 +01:00
|
|
|
hb_aat_apply_context_t *c;
|
|
|
|
const UnsizedArrayOf<HBUINT32> &ligAction;
|
|
|
|
const UnsizedArrayOf<HBUINT16> &component;
|
|
|
|
const UnsizedArrayOf<GlyphID> &ligature;
|
|
|
|
unsigned int match_length;
|
|
|
|
unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
|
2018-01-14 21:03:31 +01:00
|
|
|
};
|
|
|
|
|
2018-01-18 01:59:55 +01:00
|
|
|
inline bool apply (hb_aat_apply_context_t *c) const
|
2018-01-09 17:55:17 +01:00
|
|
|
{
|
|
|
|
TRACE_APPLY (this);
|
2018-01-14 21:03:31 +01:00
|
|
|
|
2018-01-20 03:08:56 +01:00
|
|
|
driver_context_t dc (this, c);
|
2018-01-14 21:03:31 +01:00
|
|
|
|
|
|
|
StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
|
|
|
|
driver.drive (&dc);
|
|
|
|
|
|
|
|
return_trace (dc.ret);
|
2018-01-09 17:55:17 +01:00
|
|
|
}
|
|
|
|
|
2017-08-15 02:24:36 +02:00
|
|
|
inline bool sanitize (hb_sanitize_context_t *c) const
|
|
|
|
{
|
|
|
|
TRACE_SANITIZE (this);
|
2018-01-20 03:08:56 +01:00
|
|
|
/* The rest of array sanitizations are done at run-time. */
|
2018-02-08 22:11:28 +01:00
|
|
|
return_trace (c->check_struct (this) && machine.sanitize (c) &&
|
|
|
|
ligAction && component && ligature);
|
2017-08-15 02:24:36 +02:00
|
|
|
}
|
2018-01-14 21:03:31 +01:00
|
|
|
|
|
|
|
protected:
|
2018-04-11 16:06:09 +02:00
|
|
|
StateTable<EntryData>
|
|
|
|
machine;
|
2018-09-15 19:43:33 +02:00
|
|
|
LOffsetTo<UnsizedArrayOf<HBUINT32>, false>
|
2018-01-14 21:03:31 +01:00
|
|
|
ligAction; /* Offset to the ligature action table. */
|
2018-09-15 19:43:33 +02:00
|
|
|
LOffsetTo<UnsizedArrayOf<HBUINT16>, false>
|
2018-01-14 21:03:31 +01:00
|
|
|
component; /* Offset to the component table. */
|
2018-09-15 19:43:33 +02:00
|
|
|
LOffsetTo<UnsizedArrayOf<GlyphID>, false>
|
2018-01-14 21:03:31 +01:00
|
|
|
ligature; /* Offset to the actual ligature lists. */
|
|
|
|
public:
|
|
|
|
DEFINE_SIZE_STATIC (28);
|
2017-08-15 02:24:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
struct NoncontextualSubtable
|
|
|
|
{
|
2018-01-18 01:59:55 +01:00
|
|
|
inline bool apply (hb_aat_apply_context_t *c) const
|
2018-01-09 17:55:17 +01:00
|
|
|
{
|
|
|
|
TRACE_APPLY (this);
|
2018-01-11 09:15:34 +01:00
|
|
|
|
2018-01-09 17:55:17 +01:00
|
|
|
bool ret = false;
|
2018-01-11 09:15:34 +01:00
|
|
|
unsigned int num_glyphs = c->face->get_num_glyphs ();
|
|
|
|
|
|
|
|
hb_glyph_info_t *info = c->buffer->info;
|
|
|
|
unsigned int count = c->buffer->len;
|
2018-01-09 17:55:17 +01:00
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
const GlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
|
|
|
|
if (replacement)
|
|
|
|
{
|
|
|
|
info[i].codepoint = *replacement;
|
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
}
|
2018-01-11 09:15:34 +01:00
|
|
|
|
2018-01-09 17:55:17 +01:00
|
|
|
return_trace (ret);
|
|
|
|
}
|
|
|
|
|
2017-08-15 02:24:36 +02:00
|
|
|
inline bool sanitize (hb_sanitize_context_t *c) const
|
|
|
|
{
|
|
|
|
TRACE_SANITIZE (this);
|
2018-01-09 15:48:51 +01:00
|
|
|
return_trace (substitute.sanitize (c));
|
2017-08-15 02:24:36 +02:00
|
|
|
}
|
2018-01-09 15:48:51 +01:00
|
|
|
|
|
|
|
protected:
|
|
|
|
Lookup<GlyphID> substitute;
|
|
|
|
public:
|
|
|
|
DEFINE_SIZE_MIN (2);
|
2017-08-15 02:24:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
struct InsertionSubtable
|
|
|
|
{
|
2018-09-15 14:47:18 +02:00
|
|
|
struct EntryData
|
|
|
|
{
|
|
|
|
HBUINT16 currentInsertIndex; /* Zero-based index into the insertion glyph table.
|
|
|
|
* The number of glyphs to be inserted is contained
|
|
|
|
* in the currentInsertCount field in the flags.
|
|
|
|
* A value of 0xFFFF indicates no insertion is to
|
|
|
|
* be done. */
|
|
|
|
HBUINT16 markedInsertIndex; /* Zero-based index into the insertion glyph table.
|
|
|
|
* The number of glyphs to be inserted is contained
|
|
|
|
* in the markedInsertCount field in the flags.
|
|
|
|
* A value of 0xFFFF indicates no insertion is to
|
|
|
|
* be done. */
|
|
|
|
public:
|
|
|
|
DEFINE_SIZE_STATIC (4);
|
|
|
|
};
|
|
|
|
|
|
|
|
struct driver_context_t
|
|
|
|
{
|
|
|
|
static const bool in_place = false;
|
|
|
|
enum Flags
|
|
|
|
{
|
|
|
|
SetMark = 0x8000, /* If set, mark the current glyph. */
|
|
|
|
DontAdvance = 0x4000, /* If set, don't advance to the next glyph before
|
|
|
|
* going to the new state. This does not mean
|
|
|
|
* that the glyph pointed to is the same one as
|
|
|
|
* before. If you've made insertions immediately
|
|
|
|
* downstream of the current glyph, the next glyph
|
|
|
|
* processed would in fact be the first one
|
|
|
|
* inserted. */
|
|
|
|
CurrentIsKashidaLike= 0x2000, /* If set, and the currentInsertList is nonzero,
|
|
|
|
* then the specified glyph list will be inserted
|
|
|
|
* as a kashida-like insertion, either before or
|
|
|
|
* after the current glyph (depending on the state
|
|
|
|
* of the currentInsertBefore flag). If clear, and
|
|
|
|
* the currentInsertList is nonzero, then the
|
|
|
|
* specified glyph list will be inserted as a
|
|
|
|
* split-vowel-like insertion, either before or
|
|
|
|
* after the current glyph (depending on the state
|
|
|
|
* of the currentInsertBefore flag). */
|
|
|
|
MarkedIsKashidaLike= 0x1000, /* If set, and the markedInsertList is nonzero,
|
|
|
|
* then the specified glyph list will be inserted
|
|
|
|
* as a kashida-like insertion, either before or
|
|
|
|
* after the marked glyph (depending on the state
|
|
|
|
* of the markedInsertBefore flag). If clear, and
|
|
|
|
* the markedInsertList is nonzero, then the
|
|
|
|
* specified glyph list will be inserted as a
|
|
|
|
* split-vowel-like insertion, either before or
|
|
|
|
* after the marked glyph (depending on the state
|
|
|
|
* of the markedInsertBefore flag). */
|
|
|
|
CurrentInsertBefore= 0x0800, /* If set, specifies that insertions are to be made
|
|
|
|
* to the left of the current glyph. If clear,
|
|
|
|
* they're made to the right of the current glyph. */
|
|
|
|
MarkedInsertBefore= 0x0400, /* If set, specifies that insertions are to be
|
|
|
|
* made to the left of the marked glyph. If clear,
|
|
|
|
* they're made to the right of the marked glyph. */
|
|
|
|
CurrentInsertCount= 0x3E0, /* This 5-bit field is treated as a count of the
|
|
|
|
* number of glyphs to insert at the current
|
|
|
|
* position. Since zero means no insertions, the
|
|
|
|
* largest number of insertions at any given
|
|
|
|
* current location is 31 glyphs. */
|
|
|
|
MarkedInsertCount= 0x001F, /* This 5-bit field is treated as a count of the
|
|
|
|
* number of glyphs to insert at the marked
|
|
|
|
* position. Since zero means no insertions, the
|
|
|
|
* largest number of insertions at any given
|
|
|
|
* marked location is 31 glyphs. */
|
|
|
|
};
|
|
|
|
|
|
|
|
inline driver_context_t (const InsertionSubtable *table,
|
|
|
|
hb_aat_apply_context_t *c_) :
|
|
|
|
ret (false),
|
|
|
|
c (c_),
|
2018-10-04 09:24:08 +02:00
|
|
|
mark_set (false),
|
2018-09-15 14:47:18 +02:00
|
|
|
mark (0),
|
|
|
|
insertionAction (table+table->insertionAction) {}
|
|
|
|
|
|
|
|
inline bool is_actionable (StateTableDriver<EntryData> *driver,
|
|
|
|
const Entry<EntryData> *entry)
|
|
|
|
{
|
|
|
|
return (entry->flags & (CurrentInsertCount | MarkedInsertCount)) &&
|
|
|
|
(entry->data.currentInsertIndex != 0xFFFF ||entry->data.markedInsertIndex != 0xFFFF);
|
|
|
|
}
|
|
|
|
inline bool transition (StateTableDriver<EntryData> *driver,
|
|
|
|
const Entry<EntryData> *entry)
|
|
|
|
{
|
|
|
|
hb_buffer_t *buffer = driver->buffer;
|
|
|
|
unsigned int flags = entry->flags;
|
|
|
|
|
2018-10-04 09:24:08 +02:00
|
|
|
if (entry->data.markedInsertIndex != 0xFFFF && mark_set)
|
2018-09-15 14:47:18 +02:00
|
|
|
{
|
2018-09-19 22:02:56 +02:00
|
|
|
unsigned int count = (flags & MarkedInsertCount);
|
2018-09-17 19:29:34 +02:00
|
|
|
unsigned int start = entry->data.markedInsertIndex;
|
|
|
|
const GlyphID *glyphs = &insertionAction[start];
|
|
|
|
if (unlikely (!c->sanitizer.check_array (glyphs, count))) return false;
|
2018-09-15 14:47:18 +02:00
|
|
|
|
2018-09-19 22:02:56 +02:00
|
|
|
bool before = flags & MarkedInsertBefore;
|
2018-09-15 14:47:18 +02:00
|
|
|
|
|
|
|
unsigned int end = buffer->out_len;
|
2018-09-17 19:29:34 +02:00
|
|
|
buffer->move_to (mark);
|
2018-09-15 14:47:18 +02:00
|
|
|
|
2018-09-17 19:29:34 +02:00
|
|
|
if (!before)
|
|
|
|
buffer->copy_glyph ();
|
|
|
|
/* TODO We ignore KashidaLike setting. */
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
buffer->output_glyph (glyphs[i]);
|
|
|
|
if (!before)
|
|
|
|
buffer->skip_glyph ();
|
2018-09-15 14:47:18 +02:00
|
|
|
|
2018-09-17 19:29:34 +02:00
|
|
|
buffer->move_to (end + count);
|
2018-09-28 16:00:07 +02:00
|
|
|
|
|
|
|
buffer->unsafe_to_break_from_outbuffer (mark, MIN (buffer->idx + 1, buffer->len));
|
2018-09-17 19:29:34 +02:00
|
|
|
}
|
2018-09-15 14:47:18 +02:00
|
|
|
|
2018-09-17 19:29:34 +02:00
|
|
|
if (entry->data.currentInsertIndex != 0xFFFF)
|
|
|
|
{
|
2018-09-19 22:02:56 +02:00
|
|
|
unsigned int count = (flags & CurrentInsertCount) >> 5;
|
2018-09-17 19:29:34 +02:00
|
|
|
unsigned int start = entry->data.currentInsertIndex;
|
|
|
|
const GlyphID *glyphs = &insertionAction[start];
|
|
|
|
if (unlikely (!c->sanitizer.check_array (glyphs, count))) return false;
|
2018-09-15 14:47:18 +02:00
|
|
|
|
2018-09-19 22:02:56 +02:00
|
|
|
bool before = flags & CurrentInsertBefore;
|
2018-09-15 14:47:18 +02:00
|
|
|
|
2018-09-17 19:29:34 +02:00
|
|
|
unsigned int end = buffer->out_len;
|
2018-09-15 14:47:18 +02:00
|
|
|
|
2018-09-17 19:29:34 +02:00
|
|
|
if (!before)
|
|
|
|
buffer->copy_glyph ();
|
|
|
|
/* TODO We ignore KashidaLike setting. */
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
buffer->output_glyph (glyphs[i]);
|
|
|
|
if (!before)
|
|
|
|
buffer->skip_glyph ();
|
2018-09-15 14:47:18 +02:00
|
|
|
|
2018-09-19 22:02:56 +02:00
|
|
|
/* Humm. Not sure where to move to. There's this wording under
|
|
|
|
* DontAdvance flag:
|
|
|
|
*
|
|
|
|
* "If set, don't update the glyph index before going to the new state.
|
|
|
|
* This does not mean that the glyph pointed to is the same one as
|
|
|
|
* before. If you've made insertions immediately downstream of the
|
|
|
|
* current glyph, the next glyph processed would in fact be the first
|
|
|
|
* one inserted."
|
|
|
|
*
|
2018-10-08 02:36:46 +02:00
|
|
|
* This suggests that if DontAdvance is NOT set, we should move to
|
|
|
|
* end+count. If it *was*, then move to end, such that newly inserted
|
|
|
|
* glyphs are now visible.
|
2018-10-08 00:06:53 +02:00
|
|
|
*
|
2018-10-08 02:36:46 +02:00
|
|
|
* https://github.com/harfbuzz/harfbuzz/issues/1224#issuecomment-427691417
|
2018-09-19 22:02:56 +02:00
|
|
|
*/
|
2018-10-08 02:36:46 +02:00
|
|
|
buffer->move_to ((flags & DontAdvance) ? end : end + count);
|
2018-09-15 14:47:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & SetMark)
|
2018-10-04 09:24:08 +02:00
|
|
|
{
|
|
|
|
mark_set = true;
|
2018-09-17 19:29:34 +02:00
|
|
|
mark = buffer->out_len;
|
2018-10-04 09:24:08 +02:00
|
|
|
}
|
2018-09-15 14:47:18 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
bool ret;
|
|
|
|
private:
|
|
|
|
hb_aat_apply_context_t *c;
|
2018-10-04 09:24:08 +02:00
|
|
|
bool mark_set;
|
2018-09-15 14:47:18 +02:00
|
|
|
unsigned int mark;
|
|
|
|
const UnsizedArrayOf<GlyphID> &insertionAction;
|
|
|
|
};
|
|
|
|
|
2018-01-18 01:59:55 +01:00
|
|
|
inline bool apply (hb_aat_apply_context_t *c) const
|
2018-01-09 17:55:17 +01:00
|
|
|
{
|
|
|
|
TRACE_APPLY (this);
|
2018-09-15 14:47:18 +02:00
|
|
|
|
|
|
|
driver_context_t dc (this, c);
|
|
|
|
|
|
|
|
StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
|
|
|
|
driver.drive (&dc);
|
|
|
|
|
|
|
|
return_trace (dc.ret);
|
2018-01-09 17:55:17 +01:00
|
|
|
}
|
|
|
|
|
2017-08-15 02:24:36 +02:00
|
|
|
inline bool sanitize (hb_sanitize_context_t *c) const
|
|
|
|
{
|
|
|
|
TRACE_SANITIZE (this);
|
2018-09-15 14:47:18 +02:00
|
|
|
/* The rest of array sanitizations are done at run-time. */
|
|
|
|
return_trace (c->check_struct (this) && machine.sanitize (c) &&
|
|
|
|
insertionAction);
|
2017-08-15 02:24:36 +02:00
|
|
|
}
|
2018-09-15 14:47:18 +02:00
|
|
|
|
|
|
|
protected:
|
|
|
|
StateTable<EntryData>
|
|
|
|
machine;
|
2018-09-15 19:43:33 +02:00
|
|
|
LOffsetTo<UnsizedArrayOf<GlyphID>, false>
|
2018-09-15 14:47:18 +02:00
|
|
|
insertionAction; /* Byte offset from stateHeader to the start of
|
|
|
|
* the insertion glyph table. */
|
|
|
|
public:
|
|
|
|
DEFINE_SIZE_STATIC (20);
|
2017-08-15 02:24:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct Feature
|
|
|
|
{
|
|
|
|
inline bool sanitize (hb_sanitize_context_t *c) const
|
|
|
|
{
|
|
|
|
TRACE_SANITIZE (this);
|
|
|
|
return_trace (c->check_struct (this));
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
2018-01-10 03:07:30 +01:00
|
|
|
HBUINT16 featureType; /* The type of feature. */
|
|
|
|
HBUINT16 featureSetting; /* The feature's setting (aka selector). */
|
|
|
|
HBUINT32 enableFlags; /* Flags for the settings that this feature
|
2017-08-15 02:24:36 +02:00
|
|
|
* and setting enables. */
|
2018-01-10 03:07:30 +01:00
|
|
|
HBUINT32 disableFlags; /* Complement of flags for the settings that this
|
2017-08-15 02:24:36 +02:00
|
|
|
* feature and setting disable. */
|
|
|
|
|
|
|
|
public:
|
|
|
|
DEFINE_SIZE_STATIC (12);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct ChainSubtable
|
|
|
|
{
|
2018-01-11 22:43:57 +01:00
|
|
|
friend struct Chain;
|
2017-08-15 02:24:36 +02:00
|
|
|
|
|
|
|
inline unsigned int get_size (void) const { return length; }
|
2018-09-19 22:24:34 +02:00
|
|
|
inline unsigned int get_type (void) const { return coverage & SubtableType; }
|
2017-08-15 02:24:36 +02:00
|
|
|
|
2018-09-19 22:24:34 +02:00
|
|
|
enum Coverage
|
|
|
|
{
|
|
|
|
Vertical = 0x80000000, /* If set, this subtable will only be applied
|
|
|
|
* to vertical text. If clear, this subtable
|
|
|
|
* will only be applied to horizontal text. */
|
|
|
|
Descending = 0x40000000, /* If set, this subtable will process glyphs
|
|
|
|
* in descending order. If clear, it will
|
|
|
|
* process the glyphs in ascending order. */
|
|
|
|
AllDirections = 0x20000000, /* If set, this subtable will be applied to
|
|
|
|
* both horizontal and vertical text (i.e.
|
|
|
|
* the state of bit 0x80000000 is ignored). */
|
|
|
|
Logical = 0x10000000, /* If set, this subtable will process glyphs
|
|
|
|
* in logical order (or reverse logical order,
|
|
|
|
* depending on the value of bit 0x80000000). */
|
|
|
|
Reserved = 0x0FFFFF00, /* Reserved, set to zero. */
|
|
|
|
SubtableType = 0x000000FF, /* Subtable type; see following table. */
|
|
|
|
};
|
2018-09-15 14:47:18 +02:00
|
|
|
enum Type
|
|
|
|
{
|
2017-08-15 02:24:36 +02:00
|
|
|
Rearrangement = 0,
|
|
|
|
Contextual = 1,
|
|
|
|
Ligature = 2,
|
|
|
|
Noncontextual = 4,
|
|
|
|
Insertion = 5
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename context_t>
|
2018-01-18 01:59:55 +01:00
|
|
|
inline typename context_t::return_t dispatch (context_t *c) const
|
2017-08-15 02:24:36 +02:00
|
|
|
{
|
|
|
|
unsigned int subtable_type = get_type ();
|
|
|
|
TRACE_DISPATCH (this, subtable_type);
|
|
|
|
switch (subtable_type) {
|
|
|
|
case Rearrangement: return_trace (c->dispatch (u.rearrangement));
|
|
|
|
case Contextual: return_trace (c->dispatch (u.contextual));
|
|
|
|
case Ligature: return_trace (c->dispatch (u.ligature));
|
|
|
|
case Noncontextual: return_trace (c->dispatch (u.noncontextual));
|
|
|
|
case Insertion: return_trace (c->dispatch (u.insertion));
|
|
|
|
default: return_trace (c->default_return_value ());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool sanitize (hb_sanitize_context_t *c) const
|
|
|
|
{
|
|
|
|
TRACE_SANITIZE (this);
|
|
|
|
if (!length.sanitize (c) ||
|
|
|
|
length < min_size ||
|
|
|
|
!c->check_range (this, length))
|
|
|
|
return_trace (false);
|
|
|
|
|
2018-01-18 01:59:55 +01:00
|
|
|
return_trace (dispatch (c));
|
2017-08-15 02:24:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2018-01-11 22:43:57 +01:00
|
|
|
HBUINT32 length; /* Total subtable length, including this header. */
|
|
|
|
HBUINT32 coverage; /* Coverage flags and subtable type. */
|
|
|
|
HBUINT32 subFeatureFlags;/* The 32-bit mask identifying which subtable this is. */
|
2017-08-15 02:24:36 +02:00
|
|
|
union {
|
2018-01-11 22:43:57 +01:00
|
|
|
RearrangementSubtable rearrangement;
|
|
|
|
ContextualSubtable contextual;
|
|
|
|
LigatureSubtable ligature;
|
|
|
|
NoncontextualSubtable noncontextual;
|
|
|
|
InsertionSubtable insertion;
|
2017-08-15 02:24:36 +02:00
|
|
|
} u;
|
|
|
|
public:
|
2018-01-12 12:04:53 +01:00
|
|
|
DEFINE_SIZE_MIN (12);
|
2017-08-15 02:24:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
struct Chain
|
|
|
|
{
|
2018-01-18 01:59:55 +01:00
|
|
|
inline void apply (hb_aat_apply_context_t *c) const
|
2018-01-09 17:55:17 +01:00
|
|
|
{
|
2018-09-15 18:31:14 +02:00
|
|
|
uint32_t flags = defaultFlags;
|
|
|
|
{
|
|
|
|
/* Compute applicable flags. TODO Should move this to planning
|
|
|
|
* stage and take user-requested features into account. */
|
|
|
|
unsigned int count = featureCount;
|
|
|
|
for (unsigned i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
const Feature &feature = featureZ[i];
|
|
|
|
if (false) /* XXX Check if feature enabled... */
|
|
|
|
{
|
|
|
|
flags &= feature.disableFlags;
|
|
|
|
flags |= feature.enableFlags;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-10 23:29:26 +02:00
|
|
|
const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (&featureZ, featureZ[0].static_size * featureCount);
|
2018-01-09 17:55:17 +01:00
|
|
|
unsigned int count = subtableCount;
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
{
|
2018-09-19 22:46:41 +02:00
|
|
|
bool reverse;
|
|
|
|
|
2018-09-15 18:31:14 +02:00
|
|
|
if (!(subtable->subFeatureFlags & flags))
|
|
|
|
goto skip;
|
|
|
|
|
2018-09-19 22:24:34 +02:00
|
|
|
if (!(subtable->coverage & ChainSubtable::AllDirections) &&
|
|
|
|
HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
|
|
|
|
bool (subtable->coverage & ChainSubtable::Vertical))
|
|
|
|
goto skip;
|
|
|
|
|
2018-09-19 22:46:41 +02:00
|
|
|
/* Buffer contents is always in logical direction. Determine if
|
|
|
|
* we need to reverse before applying this subtable. We reverse
|
|
|
|
* back after if we did reverse indeed.
|
|
|
|
*
|
|
|
|
* Quoting the spac:
|
|
|
|
* """
|
|
|
|
* Bits 28 and 30 of the coverage field control the order in which
|
|
|
|
* glyphs are processed when the subtable is run by the layout engine.
|
|
|
|
* Bit 28 is used to indicate if the glyph processing direction is
|
|
|
|
* the same as logical order or layout order. Bit 30 is used to
|
|
|
|
* indicate whether glyphs are processed forwards or backwards within
|
|
|
|
* that order.
|
|
|
|
|
|
|
|
Bit 30 Bit 28 Interpretation for Horizontal Text
|
|
|
|
0 0 The subtable is processed in layout order
|
|
|
|
(the same order as the glyphs, which is
|
|
|
|
always left-to-right).
|
|
|
|
1 0 The subtable is processed in reverse layout order
|
|
|
|
(the order opposite that of the glyphs, which is
|
|
|
|
always right-to-left).
|
|
|
|
0 1 The subtable is processed in logical order
|
|
|
|
(the same order as the characters, which may be
|
|
|
|
left-to-right or right-to-left).
|
|
|
|
1 1 The subtable is processed in reverse logical order
|
|
|
|
(the order opposite that of the characters, which
|
|
|
|
may be right-to-left or left-to-right).
|
|
|
|
*/
|
|
|
|
reverse = subtable->coverage & ChainSubtable::Logical ?
|
|
|
|
bool (subtable->coverage & ChainSubtable::Descending) :
|
|
|
|
bool (subtable->coverage & ChainSubtable::Descending) !=
|
|
|
|
HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
|
|
|
|
|
2018-02-10 20:35:17 +01:00
|
|
|
if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index))
|
2018-09-15 18:31:14 +02:00
|
|
|
goto skip;
|
2018-02-10 20:35:17 +01:00
|
|
|
|
2018-09-19 22:46:41 +02:00
|
|
|
if (reverse)
|
|
|
|
c->buffer->reverse ();
|
|
|
|
|
2018-10-10 05:18:19 +02:00
|
|
|
c->sanitizer.set_object (*subtable);
|
|
|
|
|
2018-09-15 18:31:14 +02:00
|
|
|
subtable->dispatch (c);
|
2018-02-07 18:30:18 +01:00
|
|
|
|
2018-09-19 22:46:41 +02:00
|
|
|
if (reverse)
|
|
|
|
c->buffer->reverse ();
|
|
|
|
|
2018-02-10 20:35:17 +01:00
|
|
|
(void) c->buffer->message (c->font, "end chain subtable %d", c->lookup_index);
|
|
|
|
|
2018-10-01 12:10:00 +02:00
|
|
|
if (unlikely (!c->buffer->successful)) return;
|
|
|
|
|
2018-09-15 18:31:14 +02:00
|
|
|
skip:
|
|
|
|
subtable = &StructAfter<ChainSubtable> (*subtable);
|
2018-02-07 18:30:18 +01:00
|
|
|
c->set_lookup_index (c->lookup_index + 1);
|
2018-01-09 17:55:17 +01:00
|
|
|
}
|
|
|
|
}
|
2017-08-15 02:24:36 +02:00
|
|
|
|
|
|
|
inline unsigned int get_size (void) const { return length; }
|
|
|
|
|
2018-09-15 14:51:50 +02:00
|
|
|
inline bool sanitize (hb_sanitize_context_t *c, unsigned int version) const
|
2017-08-15 02:24:36 +02:00
|
|
|
{
|
|
|
|
TRACE_SANITIZE (this);
|
|
|
|
if (!length.sanitize (c) ||
|
|
|
|
length < min_size ||
|
|
|
|
!c->check_range (this, length))
|
|
|
|
return_trace (false);
|
|
|
|
|
2018-09-10 23:29:26 +02:00
|
|
|
if (!c->check_array (featureZ.arrayZ, featureCount))
|
2017-08-15 02:24:36 +02:00
|
|
|
return_trace (false);
|
|
|
|
|
2018-09-10 23:29:26 +02:00
|
|
|
const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (&featureZ, featureZ[0].static_size * featureCount);
|
2017-08-15 02:24:36 +02:00
|
|
|
unsigned int count = subtableCount;
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
if (!subtable->sanitize (c))
|
|
|
|
return_trace (false);
|
2018-01-11 22:43:57 +01:00
|
|
|
subtable = &StructAfter<ChainSubtable> (*subtable);
|
2017-08-15 02:24:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return_trace (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2018-01-10 03:07:30 +01:00
|
|
|
HBUINT32 defaultFlags; /* The default specification for subtables. */
|
|
|
|
HBUINT32 length; /* Total byte count, including this header. */
|
2018-01-11 22:43:57 +01:00
|
|
|
HBUINT32 featureCount; /* Number of feature subtable entries. */
|
|
|
|
HBUINT32 subtableCount; /* The number of subtables in the chain. */
|
2017-08-15 02:24:36 +02:00
|
|
|
|
2018-09-10 23:29:26 +02:00
|
|
|
UnsizedArrayOf<Feature> featureZ; /* Features. */
|
|
|
|
/*ChainSubtable firstSubtable;*//* Subtables. */
|
2018-09-15 14:51:50 +02:00
|
|
|
/*subtableGlyphCoverageArray*/ /* Only if version >= 3. We don't use. */
|
2017-08-15 02:24:36 +02:00
|
|
|
|
|
|
|
public:
|
2018-01-12 12:04:53 +01:00
|
|
|
DEFINE_SIZE_MIN (16);
|
2017-08-15 02:24:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2018-10-07 20:01:33 +02:00
|
|
|
* The 'morx' Table
|
2017-08-15 02:24:36 +02:00
|
|
|
*/
|
|
|
|
|
2018-01-11 22:43:57 +01:00
|
|
|
struct morx
|
2017-08-15 02:24:36 +02:00
|
|
|
{
|
2018-03-26 09:34:30 +02:00
|
|
|
static const hb_tag_t tableTag = HB_AAT_TAG_morx;
|
2018-01-11 09:15:34 +01:00
|
|
|
|
2018-10-04 11:08:15 +02:00
|
|
|
inline bool has_data (void) const { return version != 0; }
|
|
|
|
|
2018-01-18 01:59:55 +01:00
|
|
|
inline void apply (hb_aat_apply_context_t *c) const
|
2018-01-09 17:55:17 +01:00
|
|
|
{
|
2018-10-01 12:10:00 +02:00
|
|
|
if (unlikely (!c->buffer->successful)) return;
|
2018-02-07 18:30:18 +01:00
|
|
|
c->set_lookup_index (0);
|
2018-09-10 23:29:26 +02:00
|
|
|
const Chain *chain = &firstChain;
|
2018-01-09 17:55:17 +01:00
|
|
|
unsigned int count = chainCount;
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
{
|
2018-01-18 01:59:55 +01:00
|
|
|
chain->apply (c);
|
2018-10-01 12:10:00 +02:00
|
|
|
if (unlikely (!c->buffer->successful)) return;
|
2018-01-11 22:43:57 +01:00
|
|
|
chain = &StructAfter<Chain> (*chain);
|
2018-01-09 17:55:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-15 02:24:36 +02:00
|
|
|
inline bool sanitize (hb_sanitize_context_t *c) const
|
|
|
|
{
|
|
|
|
TRACE_SANITIZE (this);
|
2018-09-15 14:51:50 +02:00
|
|
|
if (!version.sanitize (c) || version < 2 ||
|
2017-08-15 02:24:36 +02:00
|
|
|
!chainCount.sanitize (c))
|
|
|
|
return_trace (false);
|
|
|
|
|
2018-09-10 23:29:26 +02:00
|
|
|
const Chain *chain = &firstChain;
|
2017-08-15 02:24:36 +02:00
|
|
|
unsigned int count = chainCount;
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
{
|
2018-09-15 14:51:50 +02:00
|
|
|
if (!chain->sanitize (c, version))
|
2017-08-15 02:24:36 +02:00
|
|
|
return_trace (false);
|
2018-01-11 22:43:57 +01:00
|
|
|
chain = &StructAfter<Chain> (*chain);
|
2017-08-15 02:24:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return_trace (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2018-09-15 14:51:50 +02:00
|
|
|
HBUINT16 version; /* Version number of the glyph metamorphosis table.
|
|
|
|
* 2 or 3. */
|
|
|
|
HBUINT16 unused; /* Set to 0. */
|
2018-01-10 03:07:30 +01:00
|
|
|
HBUINT32 chainCount; /* Number of metamorphosis chains contained in this
|
2017-08-15 02:24:36 +02:00
|
|
|
* table. */
|
2018-09-10 23:29:26 +02:00
|
|
|
Chain firstChain; /* Chains. */
|
2017-08-15 02:24:36 +02:00
|
|
|
|
|
|
|
public:
|
|
|
|
DEFINE_SIZE_MIN (8);
|
|
|
|
};
|
|
|
|
|
|
|
|
} /* namespace AAT */
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */
|