projectText function
Projects logicalText for display under policy.
Identity unless the policy authorizes lowering AND the text contains a joiner AND a cluster parses as an emoji ZWJ sequence — everything else, including shaping-significant non-emoji ZWJ text, passes through byte-identical (property gate 10 rides on the parser's strictness).
Implementation
TextProjection projectText(
String logicalText, {
required TextPresentationPolicy policy,
}) {
if (policy.lowering != ClusterLowering.split) {
return TextProjection.identity(logicalText);
}
// Zero-allocation reject for the overwhelmingly common case: U+200D is a
// BMP code unit, so a plain code-unit scan is exact.
var hasZwj = false;
for (var i = 0; i < logicalText.length; i++) {
if (logicalText.codeUnitAt(i) == _zwjCodeUnit) {
hasZwj = true;
break;
}
}
if (!hasZwj) return TextProjection.identity(logicalText);
final display = StringBuffer();
final changed = <PreparedCluster>[];
var sourceOffset = 0;
for (final cluster in logicalText.characters) {
final components = splitEmojiZwjSequence(cluster);
if (components == null) {
display.write(cluster);
} else {
final displayStart = display.length;
final atoms = <TextRange>[];
for (final component in components) {
atoms.add(
TextRange(
start: display.length,
end: display.length + component.length,
),
);
display.write(component);
}
changed.add(
PreparedCluster(
sourceRange: TextRange(
start: sourceOffset,
end: sourceOffset + cluster.length,
),
displayRange: TextRange(start: displayStart, end: display.length),
displayAtomRanges: List.unmodifiable(atoms),
),
);
}
sourceOffset += cluster.length;
}
if (changed.isEmpty) {
// Joiners were present but nothing parsed as an emoji sequence (Arabic,
// Indic, malformed) — identity, same object, no mappings.
return TextProjection.identity(logicalText);
}
return TextProjection._(display.toString(), List.unmodifiable(changed));
}