--- old/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-aat-layout-morx-table.hh 2020-07-21 14:25:05.562862251 -0700 +++ /dev/null 2020-01-23 11:31:37.155195123 -0800 @@ -1,1159 +0,0 @@ -/* - * 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 - -#include "hb-open-type.hh" -#include "hb-aat-layout-common.hh" -#include "hb-ot-layout-common.hh" -#include "hb-aat-map.hh" - -/* - * morx -- Extended Glyph Metamorphosis - * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html - * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html - */ -#define HB_AAT_TAG_morx HB_TAG('m','o','r','x') -#define HB_AAT_TAG_mort HB_TAG('m','o','r','t') - - -namespace AAT { - -using namespace OT; - -template -struct RearrangementSubtable -{ - typedef typename Types::HBUINT HBUINT; - - typedef void EntryData; - - struct driver_context_t - { - static constexpr bool in_place = true; - enum Flags - { - MarkFirst = 0x8000, /* If set, make the current glyph the first - * glyph to be rearranged. */ - DontAdvance = 0x4000, /* If set, don't advance to the next glyph - * 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. */ - }; - - driver_context_t (const RearrangementSubtable *table HB_UNUSED) : - ret (false), - start (0), end (0) {} - - bool is_actionable (StateTableDriver *driver HB_UNUSED, - const Entry &entry) - { - return (entry.flags & Verb) && start < end; - } - void transition (StateTableDriver *driver, - const Entry &entry) - { - hb_buffer_t *buffer = driver->buffer; - unsigned int flags = entry.flags; - - if (flags & MarkFirst) - start = buffer->idx; - - if (flags & MarkLast) - end = MIN (buffer->idx + 1, buffer->len); - - 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. */ - const unsigned char map[16] = - { - 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 (2, m >> 4); - unsigned int r = MIN (2, m & 0x0F); - bool reverse_l = 3 == (m >> 4); - bool reverse_r = 3 == (m & 0x0F); - - if (end - start >= l + r) - { - buffer->merge_clusters (start, MIN (buffer->idx + 1, buffer->len)); - buffer->merge_clusters (start, end); - - hb_glyph_info_t *info = buffer->info; - hb_glyph_info_t buf[4]; - - memcpy (buf, info + start, l * sizeof (buf[0])); - memcpy (buf + 2, info + end - r, r * sizeof (buf[0])); - - if (l != r) - memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0])); - - 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]; - } - } - } - } - - public: - bool ret; - private: - unsigned int start; - unsigned int end; - }; - - bool apply (hb_aat_apply_context_t *c) const - { - TRACE_APPLY (this); - - driver_context_t dc (this); - - StateTableDriver driver (machine, c->buffer, c->face); - driver.drive (&dc); - - return_trace (dc.ret); - } - - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (machine.sanitize (c)); - } - - protected: - StateTable machine; - public: - DEFINE_SIZE_STATIC (16); -}; - -template -struct ContextualSubtable -{ - typedef typename Types::HBUINT HBUINT; - - 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). */ - public: - DEFINE_SIZE_STATIC (4); - }; - - struct driver_context_t - { - static constexpr bool in_place = true; - enum Flags - { - 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. */ - }; - - driver_context_t (const ContextualSubtable *table_, - hb_aat_apply_context_t *c_) : - ret (false), - c (c_), - mark_set (false), - mark (0), - table (table_), - subs (table+table->substitutionTables) {} - - bool is_actionable (StateTableDriver *driver, - const Entry &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; - } - void transition (StateTableDriver *driver, - const Entry &entry) - { - hb_buffer_t *buffer = driver->buffer; - - /* 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; - - const GlyphID *replacement; - - replacement = nullptr; - if (Types::extended) - { - if (entry.data.markIndex != 0xFFFF) - { - const Lookup &lookup = subs[entry.data.markIndex]; - replacement = lookup.get_value (buffer->info[mark].codepoint, driver->num_glyphs); - } - } - else - { - unsigned int offset = entry.data.markIndex + buffer->info[mark].codepoint; - const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; - replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; - if (!replacement->sanitize (&c->sanitizer) || !*replacement) - replacement = nullptr; - } - if (replacement) - { - buffer->unsafe_to_break (mark, MIN (buffer->idx + 1, buffer->len)); - buffer->info[mark].codepoint = *replacement; - ret = true; - } - - replacement = nullptr; - unsigned int idx = MIN (buffer->idx, buffer->len - 1); - if (Types::extended) - { - if (entry.data.currentIndex != 0xFFFF) - { - const Lookup &lookup = subs[entry.data.currentIndex]; - replacement = lookup.get_value (buffer->info[idx].codepoint, driver->num_glyphs); - } - } - else - { - unsigned int offset = entry.data.currentIndex + buffer->info[idx].codepoint; - const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; - replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; - if (!replacement->sanitize (&c->sanitizer) || !*replacement) - replacement = nullptr; - } - if (replacement) - { - buffer->info[idx].codepoint = *replacement; - ret = true; - } - - if (entry.flags & SetMark) - { - mark_set = true; - mark = buffer->idx; - } - } - - public: - bool ret; - private: - hb_aat_apply_context_t *c; - bool mark_set; - unsigned int mark; - const ContextualSubtable *table; - const UnsizedOffsetListOf, HBUINT, false> &subs; - }; - - bool apply (hb_aat_apply_context_t *c) const - { - TRACE_APPLY (this); - - driver_context_t dc (this, c); - - StateTableDriver driver (machine, c->buffer, c->face); - driver.drive (&dc); - - return_trace (dc.ret); - } - - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - - unsigned int num_entries = 0; - if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false); - - if (!Types::extended) - return_trace (substitutionTables.sanitize (c, this, 0)); - - unsigned int num_lookups = 0; - - const Entry *entries = machine.get_entries (); - for (unsigned int i = 0; i < num_entries; i++) - { - const EntryData &data = entries[i].data; - - if (data.markIndex != 0xFFFF) - num_lookups = MAX (num_lookups, 1 + data.markIndex); - if (data.currentIndex != 0xFFFF) - num_lookups = MAX (num_lookups, 1 + data.currentIndex); - } - - return_trace (substitutionTables.sanitize (c, this, num_lookups)); - } - - protected: - StateTable - machine; - NNOffsetTo, HBUINT, false>, HBUINT> - substitutionTables; - public: - DEFINE_SIZE_STATIC (20); -}; - - -template -struct LigatureEntry; - -template <> -struct LigatureEntry -{ - enum Flags - { - 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. */ - }; - - struct EntryData - { - HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry - * for processing this group, if indicated - * by the flags. */ - public: - DEFINE_SIZE_STATIC (2); - }; - - static bool performAction (const Entry &entry) - { return entry.flags & PerformAction; } - - static unsigned int ligActionIndex (const Entry &entry) - { return entry.data.ligActionIndex; } -}; -template <> -struct LigatureEntry -{ - enum Flags - { - 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. */ - Offset = 0x3FFF, /* Byte offset from beginning of subtable to the - * ligature action list. This value must be a - * multiple of 4. */ - }; - - typedef void EntryData; - - static bool performAction (const Entry &entry) - { return entry.flags & Offset; } - - static unsigned int ligActionIndex (const Entry &entry) - { return entry.flags & Offset; } -}; - - -template -struct LigatureSubtable -{ - typedef typename Types::HBUINT HBUINT; - - typedef LigatureEntry LigatureEntryT; - typedef typename LigatureEntryT::EntryData EntryData; - - struct driver_context_t - { - static constexpr bool in_place = false; - enum - { - DontAdvance = LigatureEntryT::DontAdvance, - }; - enum LigActionFlags - { - 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. */ - }; - - driver_context_t (const LigatureSubtable *table_, - hb_aat_apply_context_t *c_) : - ret (false), - c (c_), - table (table_), - ligAction (table+table->ligAction), - component (table+table->component), - ligature (table+table->ligature), - match_length (0) {} - - bool is_actionable (StateTableDriver *driver HB_UNUSED, - const Entry &entry) - { - return LigatureEntryT::performAction (entry); - } - void transition (StateTableDriver *driver, - const Entry &entry) - { - hb_buffer_t *buffer = driver->buffer; - - DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx); - if (entry.flags & LigatureEntryT::SetComponent) - { - /* Never mark same index twice, in case DontAdvance was used... */ - if (match_length && match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] == buffer->out_len) - match_length--; - - match_positions[match_length++ % ARRAY_LENGTH (match_positions)] = buffer->out_len; - DEBUG_MSG (APPLY, nullptr, "Set component at %u", buffer->out_len); - } - - if (LigatureEntryT::performAction (entry)) - { - DEBUG_MSG (APPLY, nullptr, "Perform action with %u", match_length); - unsigned int end = buffer->out_len; - - if (unlikely (!match_length)) - return; - - if (buffer->idx >= buffer->len) - return; /* TODO Work on previous instead? */ - - unsigned int cursor = match_length; - - unsigned int action_idx = LigatureEntryT::ligActionIndex (entry); - action_idx = Types::offsetToIndex (action_idx, table, ligAction.arrayZ); - const HBUINT32 *actionData = &ligAction[action_idx]; - - unsigned int ligature_idx = 0; - unsigned int action; - do - { - if (unlikely (!cursor)) - { - /* Stack underflow. Clear the stack. */ - DEBUG_MSG (APPLY, nullptr, "Stack underflow"); - match_length = 0; - break; - } - - DEBUG_MSG (APPLY, nullptr, "Moving to stack position %u", cursor - 1); - buffer->move_to (match_positions[--cursor % ARRAY_LENGTH (match_positions)]); - - if (unlikely (!actionData->sanitize (&c->sanitizer))) break; - action = *actionData; - - uint32_t uoffset = action & LigActionOffset; - if (uoffset & 0x20000000) - uoffset |= 0xC0000000; /* Sign-extend. */ - int32_t offset = (int32_t) uoffset; - unsigned int component_idx = buffer->cur().codepoint + offset; - component_idx = Types::wordOffsetToIndex (component_idx, table, component.arrayZ); - const HBUINT16 &componentData = component[component_idx]; - if (unlikely (!componentData.sanitize (&c->sanitizer))) break; - ligature_idx += componentData; - - DEBUG_MSG (APPLY, nullptr, "Action store %u last %u", - bool (action & LigActionStore), - bool (action & LigActionLast)); - if (action & (LigActionStore | LigActionLast)) - { - ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ); - const GlyphID &ligatureData = ligature[ligature_idx]; - if (unlikely (!ligatureData.sanitize (&c->sanitizer))) break; - hb_codepoint_t lig = ligatureData; - - DEBUG_MSG (APPLY, nullptr, "Produced ligature %u", lig); - buffer->replace_glyph (lig); - - unsigned int lig_end = match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] + 1u; - /* Now go and delete all subsequent components. */ - while (match_length - 1u > cursor) - { - DEBUG_MSG (APPLY, nullptr, "Skipping ligature component"); - buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]); - buffer->replace_glyph (DELETED_GLYPH); - } - - buffer->move_to (lig_end); - buffer->merge_out_clusters (match_positions[cursor % ARRAY_LENGTH (match_positions)], buffer->out_len); - } - - actionData++; - } - while (!(action & LigActionLast)); - buffer->move_to (end); - } - } - - public: - bool ret; - private: - hb_aat_apply_context_t *c; - const LigatureSubtable *table; - const UnsizedArrayOf &ligAction; - const UnsizedArrayOf &component; - const UnsizedArrayOf &ligature; - unsigned int match_length; - unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; - }; - - bool apply (hb_aat_apply_context_t *c) const - { - TRACE_APPLY (this); - - driver_context_t dc (this, c); - - StateTableDriver driver (machine, c->buffer, c->face); - driver.drive (&dc); - - return_trace (dc.ret); - } - - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - /* The rest of array sanitizations are done at run-time. */ - return_trace (c->check_struct (this) && machine.sanitize (c) && - ligAction && component && ligature); - } - - protected: - StateTable - machine; - NNOffsetTo, HBUINT> - ligAction; /* Offset to the ligature action table. */ - NNOffsetTo, HBUINT> - component; /* Offset to the component table. */ - NNOffsetTo, HBUINT> - ligature; /* Offset to the actual ligature lists. */ - public: - DEFINE_SIZE_STATIC (28); -}; - -template -struct NoncontextualSubtable -{ - bool apply (hb_aat_apply_context_t *c) const - { - TRACE_APPLY (this); - - bool ret = false; - unsigned int num_glyphs = c->face->get_num_glyphs (); - - hb_glyph_info_t *info = c->buffer->info; - unsigned int count = c->buffer->len; - 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; - } - } - - return_trace (ret); - } - - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (substitute.sanitize (c)); - } - - protected: - Lookup substitute; - public: - DEFINE_SIZE_MIN (2); -}; - -template -struct InsertionSubtable -{ - typedef typename Types::HBUINT HBUINT; - - 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 constexpr 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. */ - }; - - driver_context_t (const InsertionSubtable *table, - hb_aat_apply_context_t *c_) : - ret (false), - c (c_), - mark (0), - insertionAction (table+table->insertionAction) {} - - bool is_actionable (StateTableDriver *driver HB_UNUSED, - const Entry &entry) - { - return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) && - (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF); - } - void transition (StateTableDriver *driver, - const Entry &entry) - { - hb_buffer_t *buffer = driver->buffer; - unsigned int flags = entry.flags; - - unsigned mark_loc = buffer->out_len; - - if (entry.data.markedInsertIndex != 0xFFFF) - { - unsigned int count = (flags & MarkedInsertCount); - unsigned int start = entry.data.markedInsertIndex; - const GlyphID *glyphs = &insertionAction[start]; - if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; - - bool before = flags & MarkedInsertBefore; - - unsigned int end = buffer->out_len; - buffer->move_to (mark); - - if (buffer->idx < buffer->len && !before) - buffer->copy_glyph (); - /* TODO We ignore KashidaLike setting. */ - for (unsigned int i = 0; i < count; i++) - buffer->output_glyph (glyphs[i]); - if (buffer->idx < buffer->len && !before) - buffer->skip_glyph (); - - buffer->move_to (end + count); - - buffer->unsafe_to_break_from_outbuffer (mark, MIN (buffer->idx + 1, buffer->len)); - } - - if (flags & SetMark) - mark = mark_loc; - - if (entry.data.currentInsertIndex != 0xFFFF) - { - unsigned int count = (flags & CurrentInsertCount) >> 5; - unsigned int start = entry.data.currentInsertIndex; - const GlyphID *glyphs = &insertionAction[start]; - if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; - - bool before = flags & CurrentInsertBefore; - - unsigned int end = buffer->out_len; - - if (buffer->idx < buffer->len && !before) - buffer->copy_glyph (); - /* TODO We ignore KashidaLike setting. */ - for (unsigned int i = 0; i < count; i++) - buffer->output_glyph (glyphs[i]); - if (buffer->idx < buffer->len && !before) - buffer->skip_glyph (); - - /* 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." - * - * 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. - * - * https://github.com/harfbuzz/harfbuzz/issues/1224#issuecomment-427691417 - */ - buffer->move_to ((flags & DontAdvance) ? end : end + count); - } - } - - public: - bool ret; - private: - hb_aat_apply_context_t *c; - unsigned int mark; - const UnsizedArrayOf &insertionAction; - }; - - bool apply (hb_aat_apply_context_t *c) const - { - TRACE_APPLY (this); - - driver_context_t dc (this, c); - - StateTableDriver driver (machine, c->buffer, c->face); - driver.drive (&dc); - - return_trace (dc.ret); - } - - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - /* The rest of array sanitizations are done at run-time. */ - return_trace (c->check_struct (this) && machine.sanitize (c) && - insertionAction); - } - - protected: - StateTable - machine; - NNOffsetTo, HBUINT> - insertionAction; /* Byte offset from stateHeader to the start of - * the insertion glyph table. */ - public: - DEFINE_SIZE_STATIC (20); -}; - - -struct Feature -{ - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this)); - } - - public: - HBUINT16 featureType; /* The type of feature. */ - HBUINT16 featureSetting; /* The feature's setting (aka selector). */ - HBUINT32 enableFlags; /* Flags for the settings that this feature - * and setting enables. */ - HBUINT32 disableFlags; /* Complement of flags for the settings that this - * feature and setting disable. */ - - public: - DEFINE_SIZE_STATIC (12); -}; - -template -struct ChainSubtable -{ - typedef typename Types::HBUINT HBUINT; - - template - friend struct Chain; - - unsigned int get_size () const { return length; } - unsigned int get_type () const { return coverage & 0xFF; } - unsigned int get_coverage () const { return coverage >> (sizeof (HBUINT) * 8 - 8); } - - enum Coverage - { - Vertical = 0x80, /* If set, this subtable will only be applied - * to vertical text. If clear, this subtable - * will only be applied to horizontal text. */ - Backwards = 0x40, /* If set, this subtable will process glyphs - * in descending order. If clear, it will - * process the glyphs in ascending order. */ - AllDirections = 0x20, /* If set, this subtable will be applied to - * both horizontal and vertical text (i.e. - * the state of bit 0x80000000 is ignored). */ - Logical = 0x10, /* If set, this subtable will process glyphs - * in logical order (or reverse logical order, - * depending on the value of bit 0x80000000). */ - }; - enum Type - { - Rearrangement = 0, - Contextual = 1, - Ligature = 2, - Noncontextual = 4, - Insertion = 5 - }; - - template - typename context_t::return_t dispatch (context_t *c) const - { - 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 ()); - } - } - - bool apply (hb_aat_apply_context_t *c) const - { - TRACE_APPLY (this); - hb_sanitize_with_object_t with (&c->sanitizer, this); - return_trace (dispatch (c)); - } - - 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); - - hb_sanitize_with_object_t with (c, this); - return_trace (dispatch (c)); - } - - protected: - HBUINT length; /* Total subtable length, including this header. */ - HBUINT coverage; /* Coverage flags and subtable type. */ - HBUINT32 subFeatureFlags;/* The 32-bit mask identifying which subtable this is. */ - union { - RearrangementSubtable rearrangement; - ContextualSubtable contextual; - LigatureSubtable ligature; - NoncontextualSubtable noncontextual; - InsertionSubtable insertion; - } u; - public: - DEFINE_SIZE_MIN (2 * sizeof (HBUINT) + 4); -}; - -template -struct Chain -{ - typedef typename Types::HBUINT HBUINT; - - hb_mask_t compile_flags (const hb_aat_map_builder_t *map) const - { - hb_mask_t flags = defaultFlags; - { - unsigned int count = featureCount; - for (unsigned i = 0; i < count; i++) - { - const Feature &feature = featureZ[i]; - hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType; - hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting; - retry: - const hb_aat_map_builder_t::feature_info_t *info = map->features.bsearch (type); - if (info && info->setting == setting) - { - flags &= feature.disableFlags; - flags |= feature.enableFlags; - } - else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE && setting == HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS) - { - /* Deprecated. https://github.com/harfbuzz/harfbuzz/issues/1342 */ - type = HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE; - setting = HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS; - goto retry; - } - } - } - return flags; - } - - void apply (hb_aat_apply_context_t *c, - hb_mask_t flags) const - { - const ChainSubtable *subtable = &StructAfter > (featureZ.as_array (featureCount)); - unsigned int count = subtableCount; - for (unsigned int i = 0; i < count; i++) - { - bool reverse; - - if (!(subtable->subFeatureFlags & flags)) - goto skip; - - if (!(subtable->get_coverage() & ChainSubtable::AllDirections) && - HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) != - bool (subtable->get_coverage() & ChainSubtable::Vertical)) - goto skip; - - /* 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->get_coverage () & ChainSubtable::Logical ? - bool (subtable->get_coverage () & ChainSubtable::Backwards) : - bool (subtable->get_coverage () & ChainSubtable::Backwards) != - HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); - - if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index)) - goto skip; - - if (reverse) - c->buffer->reverse (); - - subtable->apply (c); - - if (reverse) - c->buffer->reverse (); - - (void) c->buffer->message (c->font, "end chain subtable %d", c->lookup_index); - - if (unlikely (!c->buffer->successful)) return; - - skip: - subtable = &StructAfter > (*subtable); - c->set_lookup_index (c->lookup_index + 1); - } - } - - unsigned int get_size () const { return length; } - - bool sanitize (hb_sanitize_context_t *c, unsigned int version HB_UNUSED) const - { - TRACE_SANITIZE (this); - if (!length.sanitize (c) || - length < min_size || - !c->check_range (this, length)) - return_trace (false); - - if (!c->check_array (featureZ.arrayZ, featureCount)) - return_trace (false); - - const ChainSubtable *subtable = &StructAfter > (featureZ.as_array (featureCount)); - unsigned int count = subtableCount; - for (unsigned int i = 0; i < count; i++) - { - if (!subtable->sanitize (c)) - return_trace (false); - subtable = &StructAfter > (*subtable); - } - - return_trace (true); - } - - protected: - HBUINT32 defaultFlags; /* The default specification for subtables. */ - HBUINT32 length; /* Total byte count, including this header. */ - HBUINT featureCount; /* Number of feature subtable entries. */ - HBUINT subtableCount; /* The number of subtables in the chain. */ - - UnsizedArrayOf featureZ; /* Features. */ -/*ChainSubtable firstSubtable;*//* Subtables. */ -/*subtableGlyphCoverageArray*/ /* Only if version >= 3. We don't use. */ - - public: - DEFINE_SIZE_MIN (8 + 2 * sizeof (HBUINT)); -}; - - -/* - * The 'mort'/'morx' Table - */ - -template -struct mortmorx -{ - static constexpr hb_tag_t tableTag = HB_AAT_TAG_morx; - - bool has_data () const { return version != 0; } - - void compile_flags (const hb_aat_map_builder_t *mapper, - hb_aat_map_t *map) const - { - const Chain *chain = &firstChain; - unsigned int count = chainCount; - for (unsigned int i = 0; i < count; i++) - { - map->chain_flags.push (chain->compile_flags (mapper)); - chain = &StructAfter > (*chain); - } - } - - void apply (hb_aat_apply_context_t *c) const - { - if (unlikely (!c->buffer->successful)) return; - c->set_lookup_index (0); - const Chain *chain = &firstChain; - unsigned int count = chainCount; - for (unsigned int i = 0; i < count; i++) - { - chain->apply (c, c->plan->aat_map.chain_flags[i]); - if (unlikely (!c->buffer->successful)) return; - chain = &StructAfter > (*chain); - } - } - - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - if (!version.sanitize (c) || !version || !chainCount.sanitize (c)) - return_trace (false); - - const Chain *chain = &firstChain; - unsigned int count = chainCount; - for (unsigned int i = 0; i < count; i++) - { - if (!chain->sanitize (c, version)) - return_trace (false); - chain = &StructAfter > (*chain); - } - - return_trace (true); - } - - protected: - HBUINT16 version; /* Version number of the glyph metamorphosis table. - * 1, 2, or 3. */ - HBUINT16 unused; /* Set to 0. */ - HBUINT32 chainCount; /* Number of metamorphosis chains contained in this - * table. */ - Chain firstChain; /* Chains. */ - - public: - DEFINE_SIZE_MIN (8); -}; - -struct morx : mortmorx -{ - static constexpr hb_tag_t tableTag = HB_AAT_TAG_morx; -}; -struct mort : mortmorx -{ - static constexpr hb_tag_t tableTag = HB_AAT_TAG_mort; -}; - - -} /* namespace AAT */ - - -#endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */ --- /dev/null 2020-01-23 11:31:37.155195123 -0800 +++ new/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh 2020-07-21 14:25:05.254862256 -0700 @@ -0,0 +1,1159 @@ +/* + * 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 + +#include "hb-open-type.hh" +#include "hb-aat-layout-common.hh" +#include "hb-ot-layout-common.hh" +#include "hb-aat-map.hh" + +/* + * morx -- Extended Glyph Metamorphosis + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html + */ +#define HB_AAT_TAG_morx HB_TAG('m','o','r','x') +#define HB_AAT_TAG_mort HB_TAG('m','o','r','t') + + +namespace AAT { + +using namespace OT; + +template +struct RearrangementSubtable +{ + typedef typename Types::HBUINT HBUINT; + + typedef void EntryData; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum Flags + { + MarkFirst = 0x8000, /* If set, make the current glyph the first + * glyph to be rearranged. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph + * 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. */ + }; + + driver_context_t (const RearrangementSubtable *table HB_UNUSED) : + ret (false), + start (0), end (0) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { + return (entry.flags & Verb) && start < end; + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + unsigned int flags = entry.flags; + + if (flags & MarkFirst) + start = buffer->idx; + + if (flags & MarkLast) + end = MIN (buffer->idx + 1, buffer->len); + + 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. */ + const unsigned char map[16] = + { + 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 (2, m >> 4); + unsigned int r = MIN (2, m & 0x0F); + bool reverse_l = 3 == (m >> 4); + bool reverse_r = 3 == (m & 0x0F); + + if (end - start >= l + r) + { + buffer->merge_clusters (start, MIN (buffer->idx + 1, buffer->len)); + buffer->merge_clusters (start, end); + + hb_glyph_info_t *info = buffer->info; + hb_glyph_info_t buf[4]; + + memcpy (buf, info + start, l * sizeof (buf[0])); + memcpy (buf + 2, info + end - r, r * sizeof (buf[0])); + + if (l != r) + memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0])); + + 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]; + } + } + } + } + + public: + bool ret; + private: + unsigned int start; + unsigned int end; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (machine.sanitize (c)); + } + + protected: + StateTable machine; + public: + DEFINE_SIZE_STATIC (16); +}; + +template +struct ContextualSubtable +{ + typedef typename Types::HBUINT HBUINT; + + 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). */ + public: + DEFINE_SIZE_STATIC (4); + }; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum Flags + { + 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. */ + }; + + driver_context_t (const ContextualSubtable *table_, + hb_aat_apply_context_t *c_) : + ret (false), + c (c_), + mark_set (false), + mark (0), + table (table_), + subs (table+table->substitutionTables) {} + + bool is_actionable (StateTableDriver *driver, + const Entry &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; + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + /* 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; + + const GlyphID *replacement; + + replacement = nullptr; + if (Types::extended) + { + if (entry.data.markIndex != 0xFFFF) + { + const Lookup &lookup = subs[entry.data.markIndex]; + replacement = lookup.get_value (buffer->info[mark].codepoint, driver->num_glyphs); + } + } + else + { + unsigned int offset = entry.data.markIndex + buffer->info[mark].codepoint; + const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; + replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; + if (!replacement->sanitize (&c->sanitizer) || !*replacement) + replacement = nullptr; + } + if (replacement) + { + buffer->unsafe_to_break (mark, MIN (buffer->idx + 1, buffer->len)); + buffer->info[mark].codepoint = *replacement; + ret = true; + } + + replacement = nullptr; + unsigned int idx = MIN (buffer->idx, buffer->len - 1); + if (Types::extended) + { + if (entry.data.currentIndex != 0xFFFF) + { + const Lookup &lookup = subs[entry.data.currentIndex]; + replacement = lookup.get_value (buffer->info[idx].codepoint, driver->num_glyphs); + } + } + else + { + unsigned int offset = entry.data.currentIndex + buffer->info[idx].codepoint; + const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; + replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; + if (!replacement->sanitize (&c->sanitizer) || !*replacement) + replacement = nullptr; + } + if (replacement) + { + buffer->info[idx].codepoint = *replacement; + ret = true; + } + + if (entry.flags & SetMark) + { + mark_set = true; + mark = buffer->idx; + } + } + + public: + bool ret; + private: + hb_aat_apply_context_t *c; + bool mark_set; + unsigned int mark; + const ContextualSubtable *table; + const UnsizedOffsetListOf, HBUINT, false> &subs; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + unsigned int num_entries = 0; + if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false); + + if (!Types::extended) + return_trace (substitutionTables.sanitize (c, this, 0)); + + unsigned int num_lookups = 0; + + const Entry *entries = machine.get_entries (); + for (unsigned int i = 0; i < num_entries; i++) + { + const EntryData &data = entries[i].data; + + if (data.markIndex != 0xFFFF) + num_lookups = MAX (num_lookups, 1 + data.markIndex); + if (data.currentIndex != 0xFFFF) + num_lookups = MAX (num_lookups, 1 + data.currentIndex); + } + + return_trace (substitutionTables.sanitize (c, this, num_lookups)); + } + + protected: + StateTable + machine; + NNOffsetTo, HBUINT, false>, HBUINT> + substitutionTables; + public: + DEFINE_SIZE_STATIC (20); +}; + + +template +struct LigatureEntry; + +template <> +struct LigatureEntry +{ + enum Flags + { + 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. */ + }; + + struct EntryData + { + HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry + * for processing this group, if indicated + * by the flags. */ + public: + DEFINE_SIZE_STATIC (2); + }; + + static bool performAction (const Entry &entry) + { return entry.flags & PerformAction; } + + static unsigned int ligActionIndex (const Entry &entry) + { return entry.data.ligActionIndex; } +}; +template <> +struct LigatureEntry +{ + enum Flags + { + 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. */ + Offset = 0x3FFF, /* Byte offset from beginning of subtable to the + * ligature action list. This value must be a + * multiple of 4. */ + }; + + typedef void EntryData; + + static bool performAction (const Entry &entry) + { return entry.flags & Offset; } + + static unsigned int ligActionIndex (const Entry &entry) + { return entry.flags & Offset; } +}; + + +template +struct LigatureSubtable +{ + typedef typename Types::HBUINT HBUINT; + + typedef LigatureEntry LigatureEntryT; + typedef typename LigatureEntryT::EntryData EntryData; + + struct driver_context_t + { + static constexpr bool in_place = false; + enum + { + DontAdvance = LigatureEntryT::DontAdvance, + }; + enum LigActionFlags + { + 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. */ + }; + + driver_context_t (const LigatureSubtable *table_, + hb_aat_apply_context_t *c_) : + ret (false), + c (c_), + table (table_), + ligAction (table+table->ligAction), + component (table+table->component), + ligature (table+table->ligature), + match_length (0) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { + return LigatureEntryT::performAction (entry); + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx); + if (entry.flags & LigatureEntryT::SetComponent) + { + /* Never mark same index twice, in case DontAdvance was used... */ + if (match_length && match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] == buffer->out_len) + match_length--; + + match_positions[match_length++ % ARRAY_LENGTH (match_positions)] = buffer->out_len; + DEBUG_MSG (APPLY, nullptr, "Set component at %u", buffer->out_len); + } + + if (LigatureEntryT::performAction (entry)) + { + DEBUG_MSG (APPLY, nullptr, "Perform action with %u", match_length); + unsigned int end = buffer->out_len; + + if (unlikely (!match_length)) + return; + + if (buffer->idx >= buffer->len) + return; /* TODO Work on previous instead? */ + + unsigned int cursor = match_length; + + unsigned int action_idx = LigatureEntryT::ligActionIndex (entry); + action_idx = Types::offsetToIndex (action_idx, table, ligAction.arrayZ); + const HBUINT32 *actionData = &ligAction[action_idx]; + + unsigned int ligature_idx = 0; + unsigned int action; + do + { + if (unlikely (!cursor)) + { + /* Stack underflow. Clear the stack. */ + DEBUG_MSG (APPLY, nullptr, "Stack underflow"); + match_length = 0; + break; + } + + DEBUG_MSG (APPLY, nullptr, "Moving to stack position %u", cursor - 1); + buffer->move_to (match_positions[--cursor % ARRAY_LENGTH (match_positions)]); + + if (unlikely (!actionData->sanitize (&c->sanitizer))) break; + action = *actionData; + + uint32_t uoffset = action & LigActionOffset; + if (uoffset & 0x20000000) + uoffset |= 0xC0000000; /* Sign-extend. */ + int32_t offset = (int32_t) uoffset; + unsigned int component_idx = buffer->cur().codepoint + offset; + component_idx = Types::wordOffsetToIndex (component_idx, table, component.arrayZ); + const HBUINT16 &componentData = component[component_idx]; + if (unlikely (!componentData.sanitize (&c->sanitizer))) break; + ligature_idx += componentData; + + DEBUG_MSG (APPLY, nullptr, "Action store %u last %u", + bool (action & LigActionStore), + bool (action & LigActionLast)); + if (action & (LigActionStore | LigActionLast)) + { + ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ); + const GlyphID &ligatureData = ligature[ligature_idx]; + if (unlikely (!ligatureData.sanitize (&c->sanitizer))) break; + hb_codepoint_t lig = ligatureData; + + DEBUG_MSG (APPLY, nullptr, "Produced ligature %u", lig); + buffer->replace_glyph (lig); + + unsigned int lig_end = match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] + 1u; + /* Now go and delete all subsequent components. */ + while (match_length - 1u > cursor) + { + DEBUG_MSG (APPLY, nullptr, "Skipping ligature component"); + buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]); + buffer->replace_glyph (DELETED_GLYPH); + } + + buffer->move_to (lig_end); + buffer->merge_out_clusters (match_positions[cursor % ARRAY_LENGTH (match_positions)], buffer->out_len); + } + + actionData++; + } + while (!(action & LigActionLast)); + buffer->move_to (end); + } + } + + public: + bool ret; + private: + hb_aat_apply_context_t *c; + const LigatureSubtable *table; + const UnsizedArrayOf &ligAction; + const UnsizedArrayOf &component; + const UnsizedArrayOf &ligature; + unsigned int match_length; + unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (c->check_struct (this) && machine.sanitize (c) && + ligAction && component && ligature); + } + + protected: + StateTable + machine; + NNOffsetTo, HBUINT> + ligAction; /* Offset to the ligature action table. */ + NNOffsetTo, HBUINT> + component; /* Offset to the component table. */ + NNOffsetTo, HBUINT> + ligature; /* Offset to the actual ligature lists. */ + public: + DEFINE_SIZE_STATIC (28); +}; + +template +struct NoncontextualSubtable +{ + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + bool ret = false; + unsigned int num_glyphs = c->face->get_num_glyphs (); + + hb_glyph_info_t *info = c->buffer->info; + unsigned int count = c->buffer->len; + 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; + } + } + + return_trace (ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (substitute.sanitize (c)); + } + + protected: + Lookup substitute; + public: + DEFINE_SIZE_MIN (2); +}; + +template +struct InsertionSubtable +{ + typedef typename Types::HBUINT HBUINT; + + 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 constexpr 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. */ + }; + + driver_context_t (const InsertionSubtable *table, + hb_aat_apply_context_t *c_) : + ret (false), + c (c_), + mark (0), + insertionAction (table+table->insertionAction) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { + return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) && + (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF); + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + unsigned int flags = entry.flags; + + unsigned mark_loc = buffer->out_len; + + if (entry.data.markedInsertIndex != 0xFFFF) + { + unsigned int count = (flags & MarkedInsertCount); + unsigned int start = entry.data.markedInsertIndex; + const GlyphID *glyphs = &insertionAction[start]; + if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; + + bool before = flags & MarkedInsertBefore; + + unsigned int end = buffer->out_len; + buffer->move_to (mark); + + if (buffer->idx < buffer->len && !before) + buffer->copy_glyph (); + /* TODO We ignore KashidaLike setting. */ + for (unsigned int i = 0; i < count; i++) + buffer->output_glyph (glyphs[i]); + if (buffer->idx < buffer->len && !before) + buffer->skip_glyph (); + + buffer->move_to (end + count); + + buffer->unsafe_to_break_from_outbuffer (mark, MIN (buffer->idx + 1, buffer->len)); + } + + if (flags & SetMark) + mark = mark_loc; + + if (entry.data.currentInsertIndex != 0xFFFF) + { + unsigned int count = (flags & CurrentInsertCount) >> 5; + unsigned int start = entry.data.currentInsertIndex; + const GlyphID *glyphs = &insertionAction[start]; + if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; + + bool before = flags & CurrentInsertBefore; + + unsigned int end = buffer->out_len; + + if (buffer->idx < buffer->len && !before) + buffer->copy_glyph (); + /* TODO We ignore KashidaLike setting. */ + for (unsigned int i = 0; i < count; i++) + buffer->output_glyph (glyphs[i]); + if (buffer->idx < buffer->len && !before) + buffer->skip_glyph (); + + /* 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." + * + * 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. + * + * https://github.com/harfbuzz/harfbuzz/issues/1224#issuecomment-427691417 + */ + buffer->move_to ((flags & DontAdvance) ? end : end + count); + } + } + + public: + bool ret; + private: + hb_aat_apply_context_t *c; + unsigned int mark; + const UnsizedArrayOf &insertionAction; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (c->check_struct (this) && machine.sanitize (c) && + insertionAction); + } + + protected: + StateTable + machine; + NNOffsetTo, HBUINT> + insertionAction; /* Byte offset from stateHeader to the start of + * the insertion glyph table. */ + public: + DEFINE_SIZE_STATIC (20); +}; + + +struct Feature +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 featureType; /* The type of feature. */ + HBUINT16 featureSetting; /* The feature's setting (aka selector). */ + HBUINT32 enableFlags; /* Flags for the settings that this feature + * and setting enables. */ + HBUINT32 disableFlags; /* Complement of flags for the settings that this + * feature and setting disable. */ + + public: + DEFINE_SIZE_STATIC (12); +}; + +template +struct ChainSubtable +{ + typedef typename Types::HBUINT HBUINT; + + template + friend struct Chain; + + unsigned int get_size () const { return length; } + unsigned int get_type () const { return coverage & 0xFF; } + unsigned int get_coverage () const { return coverage >> (sizeof (HBUINT) * 8 - 8); } + + enum Coverage + { + Vertical = 0x80, /* If set, this subtable will only be applied + * to vertical text. If clear, this subtable + * will only be applied to horizontal text. */ + Backwards = 0x40, /* If set, this subtable will process glyphs + * in descending order. If clear, it will + * process the glyphs in ascending order. */ + AllDirections = 0x20, /* If set, this subtable will be applied to + * both horizontal and vertical text (i.e. + * the state of bit 0x80000000 is ignored). */ + Logical = 0x10, /* If set, this subtable will process glyphs + * in logical order (or reverse logical order, + * depending on the value of bit 0x80000000). */ + }; + enum Type + { + Rearrangement = 0, + Contextual = 1, + Ligature = 2, + Noncontextual = 4, + Insertion = 5 + }; + + template + typename context_t::return_t dispatch (context_t *c) const + { + 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 ()); + } + } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_sanitize_with_object_t with (&c->sanitizer, this); + return_trace (dispatch (c)); + } + + 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); + + hb_sanitize_with_object_t with (c, this); + return_trace (dispatch (c)); + } + + protected: + HBUINT length; /* Total subtable length, including this header. */ + HBUINT coverage; /* Coverage flags and subtable type. */ + HBUINT32 subFeatureFlags;/* The 32-bit mask identifying which subtable this is. */ + union { + RearrangementSubtable rearrangement; + ContextualSubtable contextual; + LigatureSubtable ligature; + NoncontextualSubtable noncontextual; + InsertionSubtable insertion; + } u; + public: + DEFINE_SIZE_MIN (2 * sizeof (HBUINT) + 4); +}; + +template +struct Chain +{ + typedef typename Types::HBUINT HBUINT; + + hb_mask_t compile_flags (const hb_aat_map_builder_t *map) const + { + hb_mask_t flags = defaultFlags; + { + unsigned int count = featureCount; + for (unsigned i = 0; i < count; i++) + { + const Feature &feature = featureZ[i]; + hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType; + hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting; + retry: + const hb_aat_map_builder_t::feature_info_t *info = map->features.bsearch (type); + if (info && info->setting == setting) + { + flags &= feature.disableFlags; + flags |= feature.enableFlags; + } + else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE && setting == HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS) + { + /* Deprecated. https://github.com/harfbuzz/harfbuzz/issues/1342 */ + type = HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE; + setting = HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS; + goto retry; + } + } + } + return flags; + } + + void apply (hb_aat_apply_context_t *c, + hb_mask_t flags) const + { + const ChainSubtable *subtable = &StructAfter > (featureZ.as_array (featureCount)); + unsigned int count = subtableCount; + for (unsigned int i = 0; i < count; i++) + { + bool reverse; + + if (!(subtable->subFeatureFlags & flags)) + goto skip; + + if (!(subtable->get_coverage() & ChainSubtable::AllDirections) && + HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) != + bool (subtable->get_coverage() & ChainSubtable::Vertical)) + goto skip; + + /* 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->get_coverage () & ChainSubtable::Logical ? + bool (subtable->get_coverage () & ChainSubtable::Backwards) : + bool (subtable->get_coverage () & ChainSubtable::Backwards) != + HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); + + if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index)) + goto skip; + + if (reverse) + c->buffer->reverse (); + + subtable->apply (c); + + if (reverse) + c->buffer->reverse (); + + (void) c->buffer->message (c->font, "end chain subtable %d", c->lookup_index); + + if (unlikely (!c->buffer->successful)) return; + + skip: + subtable = &StructAfter > (*subtable); + c->set_lookup_index (c->lookup_index + 1); + } + } + + unsigned int get_size () const { return length; } + + bool sanitize (hb_sanitize_context_t *c, unsigned int version HB_UNUSED) const + { + TRACE_SANITIZE (this); + if (!length.sanitize (c) || + length < min_size || + !c->check_range (this, length)) + return_trace (false); + + if (!c->check_array (featureZ.arrayZ, featureCount)) + return_trace (false); + + const ChainSubtable *subtable = &StructAfter > (featureZ.as_array (featureCount)); + unsigned int count = subtableCount; + for (unsigned int i = 0; i < count; i++) + { + if (!subtable->sanitize (c)) + return_trace (false); + subtable = &StructAfter > (*subtable); + } + + return_trace (true); + } + + protected: + HBUINT32 defaultFlags; /* The default specification for subtables. */ + HBUINT32 length; /* Total byte count, including this header. */ + HBUINT featureCount; /* Number of feature subtable entries. */ + HBUINT subtableCount; /* The number of subtables in the chain. */ + + UnsizedArrayOf featureZ; /* Features. */ +/*ChainSubtable firstSubtable;*//* Subtables. */ +/*subtableGlyphCoverageArray*/ /* Only if version >= 3. We don't use. */ + + public: + DEFINE_SIZE_MIN (8 + 2 * sizeof (HBUINT)); +}; + + +/* + * The 'mort'/'morx' Table + */ + +template +struct mortmorx +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_morx; + + bool has_data () const { return version != 0; } + + void compile_flags (const hb_aat_map_builder_t *mapper, + hb_aat_map_t *map) const + { + const Chain *chain = &firstChain; + unsigned int count = chainCount; + for (unsigned int i = 0; i < count; i++) + { + map->chain_flags.push (chain->compile_flags (mapper)); + chain = &StructAfter > (*chain); + } + } + + void apply (hb_aat_apply_context_t *c) const + { + if (unlikely (!c->buffer->successful)) return; + c->set_lookup_index (0); + const Chain *chain = &firstChain; + unsigned int count = chainCount; + for (unsigned int i = 0; i < count; i++) + { + chain->apply (c, c->plan->aat_map.chain_flags[i]); + if (unlikely (!c->buffer->successful)) return; + chain = &StructAfter > (*chain); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!version.sanitize (c) || !version || !chainCount.sanitize (c)) + return_trace (false); + + const Chain *chain = &firstChain; + unsigned int count = chainCount; + for (unsigned int i = 0; i < count; i++) + { + if (!chain->sanitize (c, version)) + return_trace (false); + chain = &StructAfter > (*chain); + } + + return_trace (true); + } + + protected: + HBUINT16 version; /* Version number of the glyph metamorphosis table. + * 1, 2, or 3. */ + HBUINT16 unused; /* Set to 0. */ + HBUINT32 chainCount; /* Number of metamorphosis chains contained in this + * table. */ + Chain firstChain; /* Chains. */ + + public: + DEFINE_SIZE_MIN (8); +}; + +struct morx : mortmorx +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_morx; +}; +struct mort : mortmorx +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_mort; +}; + + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */