/* Have we visited this lookup with the current set of glyphs? */ if (done_lookups_glyph_count->get (lookup_index) != glyphs->get_population ())
{
done_lookups_glyph_count->set (lookup_index, glyphs->get_population ());
if (!done_lookups_glyph_set->has (lookup_index))
{ if (unlikely (!done_lookups_glyph_set->set (lookup_index, hb::unique_ptr<hb_set_t> {hb_set_create ()}))) returntrue;
}
/* Return if new lookup was recursed to before. */ if (lookup_limit_exceeded ()
|| visited_lookups->in_error ()
|| visited_lookups->has (lookup_index)) // Don't increment lookup count here, that will be done in the call to closure_lookups() // made by recurse_func. return;
/* Note that GPOS sets recurse_func to nullptr already, so it doesn't get * past the previous check. For GSUB, we only want to collect the output * glyphs in the recursion. If output is not requested, we can go home now. * * Note further, that the above is not exactly correct. A recursed lookup * is allowed to match input that is not matched in the context, but that's * not how most fonts are built. It's possible to relax that and recurse * with all sets here if it proves to be an issue.
*/
if (output == hb_set_get_empty ()) return;
/* Return if new lookup was recursed to before. */ if (recursed_lookups->has (lookup_index)) return;
hb_set_t *old_before = before;
hb_set_t *old_input = input;
hb_set_t *old_after = after;
before = input = after = hb_set_get_empty ();
#ifndef HB_OPTIMIZE_SIZE
HB_ALWAYS_INLINE #endif void reset_fast (unsignedint start_index_)
{ // Doesn't set end or syllable. Used by GPOS which doesn't care / change.
idx = start_index_;
}
bool match_properties_mark (hb_codepoint_t glyph, unsignedint glyph_props, unsignedint match_props) const
{ /* If using mark filtering sets, the high short of * match_props has the set index.
*/ if (match_props & LookupFlag::UseMarkFilteringSet) return gdef_accel.mark_set_covers (match_props >> 16, glyph);
/* The second byte of match_props has the meaning * "ignore marks of attachment type different than * the attachment type specified."
*/ if (match_props & LookupFlag::MarkAttachmentType) return (match_props & LookupFlag::MarkAttachmentType) == (glyph_props & LookupFlag::MarkAttachmentType);
/* Not covered, if, for example, glyph class is ligature and * match_props includes LookupFlags::IgnoreLigatures
*/ if (glyph_props & match_props & LookupFlag::IgnoreFlags) returnfalse;
if (unlikely (glyph_props & HB_OT_LAYOUT_GLYPH_PROPS_MARK)) return match_properties_mark (info->codepoint, glyph_props, match_props);
if (new_syllables != (unsigned) -1)
buffer->cur().syllable() = new_syllables;
unsignedint props = _hb_glyph_info_get_glyph_props (&buffer->cur());
props |= HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED; if (ligature)
{
props |= HB_OT_LAYOUT_GLYPH_PROPS_LIGATED; /* In the only place that the MULTIPLIED bit is used, Uniscribe * seems to only care about the "last" transformation between * Ligature and Multiple substitutions. Ie. if you ligate, expand, * and ligate again, it forgives the multiplication and acts as * if only ligation happened. As such, clear MULTIPLIED bit.
*/
props &= ~HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED;
} if (component)
props |= HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED; if (likely (has_glyph_classes))
{
props &= HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE;
_hb_glyph_info_set_glyph_props (&buffer->cur(), props | gdef_accel.get_glyph_props (glyph_index));
} elseif (class_guess)
{
props &= HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE;
_hb_glyph_info_set_glyph_props (&buffer->cur(), props | class_guess);
} else
_hb_glyph_info_set_glyph_props (&buffer->cur(), props);
}
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE /* Cache handling * * We allow one subtable from each lookup to use a cache. The assumption * being that multiple subtables of the same lookup cannot use a cache * because the resources they would use will collide. As such, we ask * each subtable to tell us how much it costs (which a cache would avoid), * and we allocate the cache opportunity to the costliest subtable.
*/ unsigned cost = cache_cost (obj, hb_prioritize); if (cost > cache_user_cost)
{
cache_user_idx = i - 1;
cache_user_cost = cost;
} #endif
/* * This is perhaps the trickiest part of OpenType... Remarks: * * - If all components of the ligature were marks, we call this a mark ligature. * * - If there is no GDEF, and the ligature is NOT a mark ligature, we categorize * it as a ligature glyph. * * - Ligatures cannot be formed across glyphs attached to different components * of previous ligatures. Eg. the sequence is LAM,SHADDA,LAM,FATHA,HEH, and * LAM,LAM,HEH form a ligature, leaving SHADDA,FATHA next to eachother. * However, it would be wrong to ligate that SHADDA,FATHA sequence. * There are a couple of exceptions to this: * * o If a ligature tries ligating with marks that belong to it itself, go ahead, * assuming that the font designer knows what they are doing (otherwise it can * break Indic stuff when a matra wants to ligate with a conjunct, * * o If two marks want to ligate and they belong to different components of the * same ligature glyph, and said ligature glyph is to be ignored according to * mark-filtering rules, then allow. * https://github.com/harfbuzz/harfbuzz/issues/545
*/
if (first_lig_id && first_lig_comp)
{ /* If first component was attached to a previous ligature component, * all subsequent components should be attached to the same ligature
* component, otherwise we shouldn't ligate them... */ if (first_lig_id != this_lig_id || first_lig_comp != this_lig_comp)
{ /* ...unless, we are attached to a base ligature and that base
* ligature is ignorable. */ if (ligbase == LIGBASE_NOT_CHECKED)
{ bool found = false; constauto *out = buffer->out_info; unsignedint j = buffer->out_len; while (j && _hb_glyph_info_get_lig_id (&out[j - 1]) == first_lig_id)
{ if (_hb_glyph_info_get_lig_comp (&out[j - 1]) == 0)
{
j--;
found = true; break;
}
j--;
}
if (ligbase == LIGBASE_MAY_NOT_SKIP)
return_trace (false);
}
} else
{ /* If first component was NOT attached to a previous ligature component, * all subsequent components should also NOT be attached to any ligature
* component, unless they are attached to the first component itself! */ if (this_lig_id && this_lig_comp && (this_lig_id != first_lig_id))
return_trace (false);
}
return_trace (true);
} staticinlinebool ligate_input (hb_ot_apply_context_t *c, unsignedint count, /* Including the first glyph */ constunsignedint *match_positions, /* Including the first glyph */ unsignedint match_end,
hb_codepoint_t lig_glyph, unsignedint total_component_count)
{
TRACE_APPLY (nullptr);
hb_buffer_t *buffer = c->buffer;
buffer->merge_clusters (buffer->idx, match_end);
/* - If a base and one or more marks ligate, consider that as a base, NOT * ligature, such that all following marks can still attach to it. * https://github.com/harfbuzz/harfbuzz/issues/1109 * * - If all components of the ligature were marks, we call this a mark ligature. * If it *is* a mark ligature, we don't allocate a new ligature id, and leave * the ligature to keep its old ligature id. This will allow it to attach to * a base ligature in GPOS. Eg. if the sequence is: LAM,LAM,SHADDA,FATHA,HEH, * and LAM,LAM,HEH for a ligature, they will leave SHADDA and FATHA with a * ligature id and component value of 2. Then if SHADDA,FATHA form a ligature * later, we don't want them to lose their ligature id/component, otherwise * GPOS will fail to correctly position the mark ligature on top of the * LAM,LAM,HEH ligature. See: * https://bugzilla.gnome.org/show_bug.cgi?id=676343 * * - If a ligature is formed of components that some of which are also ligatures * themselves, and those ligature components had marks attached to *their* * components, we have to attach the marks to the new ligature component * positions! Now *that*'s tricky! And these marks may be following the * last component of the whole sequence, so we should loop forward looking * for them and update them. * * Eg. the sequence is LAM,LAM,SHADDA,FATHA,HEH, and the font first forms a * 'calt' ligature of LAM,HEH, leaving the SHADDA and FATHA with a ligature * id and component == 1. Now, during 'liga', the LAM and the LAM-HEH ligature * form a LAM-LAM-HEH ligature. We need to reassign the SHADDA and FATHA to * the new ligature with a component value of 2. * * This in fact happened to a font... See: * https://bugzilla.gnome.org/show_bug.cgi?id=437633
*/
bool is_base_ligature = _hb_glyph_info_is_base_glyph (&buffer->info[match_positions[0]]); bool is_mark_ligature = _hb_glyph_info_is_mark (&buffer->info[match_positions[0]]); for (unsignedint i = 1; i < count; i++) if (!_hb_glyph_info_is_mark (&buffer->info[match_positions[i]]))
{
is_base_ligature = false;
is_mark_ligature = false; break;
} bool is_ligature = !is_base_ligature && !is_mark_ligature;
if (!is_mark_ligature && last_lig_id)
{ /* Re-adjust components for any marks following. */ for (unsigned i = buffer->idx; i < buffer->len; ++i)
{ if (last_lig_id != _hb_glyph_info_get_lig_id (&buffer->info[i])) break;
unsigned this_comp = _hb_glyph_info_get_lig_comp (&buffer->info[i]); if (!this_comp) break;
HBUINT16 sequenceIndex; /* Index into current glyph
* sequence--first glyph = 0 */
HBUINT16 lookupListIndex; /* Lookup to apply to that
* position--zero--based */ public:
DEFINE_SIZE_STATIC (4);
};
staticunsigned serialize_lookuprecord_array (hb_serialize_context_t *c, const hb_array_t<const LookupRecord> lookupRecords, const hb_map_t *lookup_map)
{ unsigned count = 0; for (const LookupRecord& r : lookupRecords)
{ if (!lookup_map->has (r.lookupListIndex)) continue;
template <typename context_t> staticinlinevoid recurse_lookups (context_t *c, unsignedint lookupCount, const LookupRecord lookupRecord[] /* Array of LookupRecords--in design order */)
{ for (unsignedint i = 0; i < lookupCount; i++)
c->recurse (lookupRecord[i].lookupListIndex);
}
staticinlinevoid apply_lookup (hb_ot_apply_context_t *c, unsignedint count, /* Including the first glyph */ unsignedint *match_positions, /* Including the first glyph */ unsignedint lookupCount, const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */ unsignedint match_end)
{
hb_buffer_t *buffer = c->buffer; int end;
/* Recursed lookup changed buffer len. Adjust. * * TODO: * * Right now, if buffer length increased by n, we assume n new glyphs * were added right after the current position, and if buffer length * was decreased by n, we assume n match positions after the current * one where removed. The former (buffer length increased) case is * fine, but the decrease case can be improved in at least two ways, * both of which are significant: * * - If recursed-to lookup is MultipleSubst and buffer length * decreased, then it's current match position that was deleted, * NOT the one after it. * * - If buffer length was decreased by n, it does not necessarily * mean that n match positions where removed, as there recursed-to * lookup might had a different LookupFlag. Here's a constructed * case of that: * https://github.com/harfbuzz/harfbuzz/discussions/3538 * * It should be possible to construct tests for both of these cases.
*/
end += delta; if (end < int (match_positions[idx]))
{ /* End might end up being smaller than match_positions[idx] if the recursed * lookup ended up removing many items. * Just never rewind end beyond start of current position, since that is * not possible in the recursed lookup. Also adjust delta as such. * * https://bugs.chromium.org/p/chromium/issues/detail?id=659496 * https://github.com/harfbuzz/harfbuzz/issues/1611
*/
delta += match_positions[idx] - end;
end = match_positions[idx];
}
unsignedint next = idx + 1; /* next now is the position after the recursed lookup. */
template <typename HBUINT> staticinlinebool context_intersects (const hb_set_t *glyphs, unsignedint inputCount, /* Including the first glyph (not matched) */ const HBUINT input[], /* Array of input values--start with second glyph */
ContextClosureLookupContext &lookup_context)
{ return array_is_subset_of (glyphs,
inputCount ? inputCount - 1 : 0, input,
lookup_context.funcs.intersects,
lookup_context.intersects_data,
lookup_context.intersects_cache);
}
template <typename HBUINT> staticinlinevoid context_closure_lookup (hb_closure_context_t *c, unsignedint inputCount, /* Including the first glyph (not matched) */ const HBUINT input[], /* Array of input values--start with second glyph */ unsignedint lookupCount, const LookupRecord lookupRecord[], unsigned value, /* Index of first glyph in Coverage or Class value in ClassDef table */
ContextClosureLookupContext &lookup_context)
{ if (context_intersects (c->glyphs,
inputCount, input,
lookup_context))
context_closure_recurse_lookups (c,
inputCount, input,
lookupCount, lookupRecord,
value,
lookup_context.context_format,
lookup_context.intersects_data,
lookup_context.funcs.intersected_glyphs,
lookup_context.intersected_glyphs_cache);
}
template <typename HBUINT> staticinlinevoid context_collect_glyphs_lookup (hb_collect_glyphs_context_t *c, unsignedint inputCount, /* Including the first glyph (not matched) */ const HBUINT input[], /* Array of input values--start with second glyph */ unsignedint lookupCount, const LookupRecord lookupRecord[],
ContextCollectGlyphsLookupContext &lookup_context)
{
collect_array (c, c->input,
inputCount ? inputCount - 1 : 0, input,
lookup_context.funcs.collect, lookup_context.collect_data);
recurse_lookups (c,
lookupCount, lookupRecord);
}
template <typename HBUINT> staticinlinebool context_would_apply_lookup (hb_would_apply_context_t *c, unsignedint inputCount, /* Including the first glyph (not matched) */ const HBUINT input[], /* Array of input values--start with second glyph */ unsignedint lookupCount HB_UNUSED, const LookupRecord lookupRecord[] HB_UNUSED, const ContextApplyLookupContext &lookup_context)
{ return would_match_input (c,
inputCount, input,
lookup_context.funcs.match, lookup_context.match_data);
}
template <typename HBUINT>
HB_ALWAYS_INLINE staticbool context_apply_lookup (hb_ot_apply_context_t *c, unsignedint inputCount, /* Including the first glyph (not matched) */ const HBUINT input[], /* Array of input values--start with second glyph */ unsignedint lookupCount, const LookupRecord lookupRecord[], const ContextApplyLookupContext &lookup_context)
{ if (unlikely (inputCount > HB_MAX_CONTEXT_LENGTH)) returnfalse; unsigned match_positions_stack[4]; unsigned *match_positions = match_positions_stack; if (unlikely (inputCount > ARRAY_LENGTH (match_positions_stack)))
{
match_positions = (unsigned *) hb_malloc (hb_max (inputCount, 1u) * sizeof (match_positions[0])); if (unlikely (!match_positions)) returnfalse;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.