| #ifndef OT_LAYOUT_GSUB_LIGATURE_HH | |
| #define OT_LAYOUT_GSUB_LIGATURE_HH | |
| #include "Common.hh" | |
| namespace OT { | |
| namespace Layout { | |
| namespace GSUB_impl { | |
| template <typename Types> | |
| struct Ligature | |
| { | |
| public: | |
| typename Types::HBGlyphID | |
| ligGlyph; /* GlyphID of ligature to substitute */ | |
| HeadlessArray16Of<typename Types::HBGlyphID> | |
| component; /* Array of component GlyphIDs--start | |
| * with the second component--ordered | |
| * in writing direction */ | |
| public: | |
| DEFINE_SIZE_ARRAY (Types::size + 2, component); | |
| bool sanitize (hb_sanitize_context_t *c) const | |
| { | |
| TRACE_SANITIZE (this); | |
| return_trace (ligGlyph.sanitize (c) && component.sanitize (c)); | |
| } | |
| bool intersects (const hb_set_t *glyphs) const | |
| { return hb_all (component, glyphs); } | |
| bool intersects_lig_glyph (const hb_set_t *glyphs) const | |
| { return glyphs->has(ligGlyph); } | |
| void closure (hb_closure_context_t *c) const | |
| { | |
| if (!intersects (c->glyphs)) return; | |
| c->output->add (ligGlyph); | |
| } | |
| void collect_glyphs (hb_collect_glyphs_context_t *c) const | |
| { | |
| c->input->add_array (component.arrayZ, component.get_length ()); | |
| c->output->add (ligGlyph); | |
| } | |
| bool would_apply (hb_would_apply_context_t *c) const | |
| { | |
| if (c->len != component.lenP1) | |
| return false; | |
| for (unsigned int i = 1; i < c->len; i++) | |
| if (likely (c->glyphs[i] != component[i])) | |
| return false; | |
| return true; | |
| } | |
| bool apply (hb_ot_apply_context_t *c) const | |
| { | |
| TRACE_APPLY (this); | |
| unsigned int count = component.lenP1; | |
| if (unlikely (!count)) return_trace (false); | |
| /* Special-case to make it in-place and not consider this | |
| * as a "ligated" substitution. */ | |
| if (unlikely (count == 1)) | |
| { | |
| if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) | |
| { | |
| c->buffer->sync_so_far (); | |
| c->buffer->message (c->font, | |
| "replacing glyph at %u (ligature substitution)", | |
| c->buffer->idx); | |
| } | |
| c->replace_glyph (ligGlyph); | |
| if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) | |
| { | |
| c->buffer->message (c->font, | |
| "replaced glyph at %u (ligature substitution)", | |
| c->buffer->idx - 1u); | |
| } | |
| return_trace (true); | |
| } | |
| unsigned int total_component_count = 0; | |
| if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return false; | |
| unsigned match_positions_stack[4]; | |
| unsigned *match_positions = match_positions_stack; | |
| if (unlikely (count > ARRAY_LENGTH (match_positions_stack))) | |
| { | |
| match_positions = (unsigned *) hb_malloc (hb_max (count, 1u) * sizeof (unsigned)); | |
| if (unlikely (!match_positions)) | |
| return_trace (false); | |
| } | |
| unsigned int match_end = 0; | |
| if (likely (!match_input (c, count, | |
| &component[1], | |
| match_glyph, | |
| nullptr, | |
| &match_end, | |
| match_positions, | |
| &total_component_count))) | |
| { | |
| c->buffer->unsafe_to_concat (c->buffer->idx, match_end); | |
| if (match_positions != match_positions_stack) | |
| hb_free (match_positions); | |
| return_trace (false); | |
| } | |
| unsigned pos = 0; | |
| if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) | |
| { | |
| unsigned delta = c->buffer->sync_so_far (); | |
| pos = c->buffer->idx; | |
| char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0}; | |
| char *p = buf; | |
| match_end += delta; | |
| for (unsigned i = 0; i < count; i++) | |
| { | |
| match_positions[i] += delta; | |
| if (i) | |
| *p++ = ','; | |
| snprintf (p, sizeof(buf) - (p - buf), "%u", match_positions[i]); | |
| p += strlen(p); | |
| } | |
| c->buffer->message (c->font, | |
| "ligating glyphs at %s", | |
| buf); | |
| } | |
| ligate_input (c, | |
| count, | |
| match_positions, | |
| match_end, | |
| ligGlyph, | |
| total_component_count); | |
| if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) | |
| { | |
| c->buffer->sync_so_far (); | |
| c->buffer->message (c->font, | |
| "ligated glyph at %u", | |
| pos); | |
| } | |
| if (match_positions != match_positions_stack) | |
| hb_free (match_positions); | |
| return_trace (true); | |
| } | |
| template <typename Iterator, | |
| hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))> | |
| bool serialize (hb_serialize_context_t *c, | |
| hb_codepoint_t ligature, | |
| Iterator components /* Starting from second */) | |
| { | |
| TRACE_SERIALIZE (this); | |
| if (unlikely (!c->extend_min (this))) return_trace (false); | |
| ligGlyph = ligature; | |
| if (unlikely (!component.serialize (c, components))) return_trace (false); | |
| return_trace (true); | |
| } | |
| bool subset (hb_subset_context_t *c, unsigned coverage_idx) const | |
| { | |
| TRACE_SUBSET (this); | |
| const hb_set_t &glyphset = *c->plan->glyphset_gsub (); | |
| const hb_map_t &glyph_map = *c->plan->glyph_map; | |
| if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false); | |
| // Ensure Coverage table is always packed after this. | |
| c->serializer->add_virtual_link (coverage_idx); | |
| auto it = | |
| + hb_iter (component) | |
| | hb_map (glyph_map) | |
| ; | |
| auto *out = c->serializer->start_embed (*this); | |
| return_trace (out->serialize (c->serializer, | |
| glyph_map[ligGlyph], | |
| it)); } | |
| }; | |
| } | |
| } | |
| } | |
| #endif /* OT_LAYOUT_GSUB_LIGATURE_HH */ |