| 1 | #ifndef OT_LAYOUT_GSUB_LIGATURE_HH |
| 2 | #define OT_LAYOUT_GSUB_LIGATURE_HH |
| 3 | |
| 4 | #include "Common.hh" |
| 5 | |
| 6 | namespace OT { |
| 7 | namespace Layout { |
| 8 | namespace GSUB_impl { |
| 9 | |
| 10 | template <typename Types> |
| 11 | struct Ligature |
| 12 | { |
| 13 | public: |
| 14 | typename Types::HBGlyphID |
| 15 | ligGlyph; /* GlyphID of ligature to substitute */ |
| 16 | HeadlessArray16Of<typename Types::HBGlyphID> |
| 17 | component; /* Array of component GlyphIDs--start |
| 18 | * with the second component--ordered |
| 19 | * in writing direction */ |
| 20 | public: |
| 21 | DEFINE_SIZE_ARRAY (Types::size + 2, component); |
| 22 | |
| 23 | bool sanitize (hb_sanitize_context_t *c) const |
| 24 | { |
| 25 | TRACE_SANITIZE (this); |
| 26 | return_trace (ligGlyph.sanitize (c) && component.sanitize (c)); |
| 27 | } |
| 28 | |
| 29 | bool intersects (const hb_set_t *glyphs) const |
| 30 | { return hb_all (component, glyphs); } |
| 31 | |
| 32 | bool intersects_lig_glyph (const hb_set_t *glyphs) const |
| 33 | { return glyphs->has(ligGlyph); } |
| 34 | |
| 35 | void closure (hb_closure_context_t *c) const |
| 36 | { |
| 37 | if (!intersects (c->glyphs)) return; |
| 38 | c->output->add (ligGlyph); |
| 39 | } |
| 40 | |
| 41 | void collect_glyphs (hb_collect_glyphs_context_t *c) const |
| 42 | { |
| 43 | c->input->add_array (component.arrayZ, component.get_length ()); |
| 44 | c->output->add (ligGlyph); |
| 45 | } |
| 46 | |
| 47 | bool would_apply (hb_would_apply_context_t *c) const |
| 48 | { |
| 49 | if (c->len != component.lenP1) |
| 50 | return false; |
| 51 | |
| 52 | for (unsigned int i = 1; i < c->len; i++) |
| 53 | if (likely (c->glyphs[i] != component[i])) |
| 54 | return false; |
| 55 | |
| 56 | return true; |
| 57 | } |
| 58 | |
| 59 | bool apply (hb_ot_apply_context_t *c) const |
| 60 | { |
| 61 | TRACE_APPLY (this); |
| 62 | unsigned int count = component.lenP1; |
| 63 | |
| 64 | if (unlikely (!count)) return_trace (false); |
| 65 | |
| 66 | /* Special-case to make it in-place and not consider this |
| 67 | * as a "ligated" substitution. */ |
| 68 | if (unlikely (count == 1)) |
| 69 | { |
| 70 | |
| 71 | if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) |
| 72 | { |
| 73 | c->buffer->sync_so_far (); |
| 74 | c->buffer->message (c->font, |
| 75 | "replacing glyph at %u (ligature substitution)" , |
| 76 | c->buffer->idx); |
| 77 | } |
| 78 | |
| 79 | c->replace_glyph (ligGlyph); |
| 80 | |
| 81 | if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) |
| 82 | { |
| 83 | c->buffer->message (c->font, |
| 84 | "replaced glyph at %u (ligature substitution)" , |
| 85 | c->buffer->idx - 1u); |
| 86 | } |
| 87 | |
| 88 | return_trace (true); |
| 89 | } |
| 90 | |
| 91 | unsigned int total_component_count = 0; |
| 92 | |
| 93 | unsigned int match_end = 0; |
| 94 | unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; |
| 95 | |
| 96 | if (likely (!match_input (c, count, |
| 97 | &component[1], |
| 98 | match_glyph, |
| 99 | nullptr, |
| 100 | &match_end, |
| 101 | match_positions, |
| 102 | &total_component_count))) |
| 103 | { |
| 104 | c->buffer->unsafe_to_concat (c->buffer->idx, match_end); |
| 105 | return_trace (false); |
| 106 | } |
| 107 | |
| 108 | unsigned pos = 0; |
| 109 | if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) |
| 110 | { |
| 111 | unsigned delta = c->buffer->sync_so_far (); |
| 112 | |
| 113 | pos = c->buffer->idx; |
| 114 | |
| 115 | char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0}; |
| 116 | char *p = buf; |
| 117 | |
| 118 | match_end += delta; |
| 119 | for (unsigned i = 0; i < count; i++) |
| 120 | { |
| 121 | match_positions[i] += delta; |
| 122 | if (i) |
| 123 | *p++ = ','; |
| 124 | snprintf (p, sizeof(buf) - (p - buf), "%u" , match_positions[i]); |
| 125 | p += strlen(p); |
| 126 | } |
| 127 | |
| 128 | c->buffer->message (c->font, |
| 129 | "ligating glyphs at %s" , |
| 130 | buf); |
| 131 | } |
| 132 | |
| 133 | ligate_input (c, |
| 134 | count, |
| 135 | match_positions, |
| 136 | match_end, |
| 137 | ligGlyph, |
| 138 | total_component_count); |
| 139 | |
| 140 | if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) |
| 141 | { |
| 142 | c->buffer->sync_so_far (); |
| 143 | c->buffer->message (c->font, |
| 144 | "ligated glyph at %u" , |
| 145 | pos); |
| 146 | } |
| 147 | |
| 148 | return_trace (true); |
| 149 | } |
| 150 | |
| 151 | template <typename Iterator, |
| 152 | hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))> |
| 153 | bool serialize (hb_serialize_context_t *c, |
| 154 | hb_codepoint_t ligature, |
| 155 | Iterator components /* Starting from second */) |
| 156 | { |
| 157 | TRACE_SERIALIZE (this); |
| 158 | if (unlikely (!c->extend_min (this))) return_trace (false); |
| 159 | ligGlyph = ligature; |
| 160 | if (unlikely (!component.serialize (c, components))) return_trace (false); |
| 161 | return_trace (true); |
| 162 | } |
| 163 | |
| 164 | bool subset (hb_subset_context_t *c, unsigned coverage_idx) const |
| 165 | { |
| 166 | TRACE_SUBSET (this); |
| 167 | const hb_set_t &glyphset = *c->plan->glyphset_gsub (); |
| 168 | const hb_map_t &glyph_map = *c->plan->glyph_map; |
| 169 | |
| 170 | if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false); |
| 171 | // Ensure Coverage table is always packed after this. |
| 172 | c->serializer->add_virtual_link (coverage_idx); |
| 173 | |
| 174 | auto it = |
| 175 | + hb_iter (component) |
| 176 | | hb_map (glyph_map) |
| 177 | ; |
| 178 | |
| 179 | auto *out = c->serializer->start_embed (*this); |
| 180 | return_trace (out->serialize (c->serializer, |
| 181 | glyph_map[ligGlyph], |
| 182 | it)); } |
| 183 | }; |
| 184 | |
| 185 | |
| 186 | } |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | #endif /* OT_LAYOUT_GSUB_LIGATURE_HH */ |
| 191 | |